/*--------------------------------------------------------------------
 * guc.c
 *
 * 支持大统一配置方案，包括SET命令、配置文件和命令行选项。
 * 有关更多信息，请参见src/backend/utils/misc/README。
 *
 *
 * Copyright (c) 2000-2022, PostgreSQL Global Development Group
 * Written by Peter Eisentraut <peter_e@gmx.net>.
 *
 * IDENTIFICATION
 *	  src/backend/utils/misc/guc.c
 *
 *--------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>
#include <float.h>
#include <math.h>
#include <limits.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifndef WIN32
#include <sys/mman.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_SYSLOG
#include <syslog.h>
#endif
#include <unistd.h>

#include "access/commit_ts.h"
#include "access/gin.h"
#include "access/rmgr.h"
#include "access/tableam.h"
#include "access/toast_compression.h"
#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
#include "access/xlog_internal.h"
#include "access/xlogprefetcher.h"
#include "access/xlogrecovery.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_parameter_acl.h"
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "commands/variable.h"
#include "common/string.h"
#include "funcapi.h"
#include "jit/jit.h"
#include "libpq/auth.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "optimizer/geqo.h"
#include "optimizer/optimizer.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "parser/parser.h"
#include "parser/scansup.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgworker_internals.h"
#include "postmaster/bgwriter.h"
#include "postmaster/postmaster.h"
#include "postmaster/startup.h"
#include "postmaster/syslogger.h"
#include "postmaster/walwriter.h"
#include "replication/logicallauncher.h"
#include "replication/reorderbuffer.h"
#include "replication/slot.h"
#include "replication/syncrep.h"
#include "replication/walreceiver.h"
#include "replication/walsender.h"
#include "storage/bufmgr.h"
#include "storage/dsm_impl.h"
#include "storage/fd.h"
#include "storage/large_object.h"
#include "storage/pg_shmem.h"
#include "storage/predicate.h"
#include "storage/proc.h"
#include "storage/standby.h"
#include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h"
#include "utils/acl.h"
#include "utils/backend_status.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "utils/pg_lsn.h"
#include "utils/plancache.h"
#include "utils/portal.h"
#include "utils/ps_status.h"
#include "utils/queryjumble.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/tzparser.h"
#include "utils/inval.h"
#include "utils/varlena.h"
#include "utils/xml.h"
#ifdef FDD
#include "utils/fdb_page_encrypt.h"
#include "postmaster/syslogger.h"
#endif //FDD

#ifndef PG_KRB_SRVTAB
#define PG_KRB_SRVTAB ""
#endif

#define CONFIG_FILENAME "postgresql.conf"
#define HBA_FILENAME	"pg_hba.conf"
#define IDENT_FILENAME	"pg_ident.conf"

#ifdef EXEC_BACKEND
#define CONFIG_EXEC_PARAMS "global/config_exec_params"
#define CONFIG_EXEC_PARAMS_NEW "global/config_exec_params.new"
#endif

/*
 * REAL类型的guc值在GUC序列化中的打印精度。
 */
#define REALTYPE_PRECISION 17

/* XXX 这些应该出现在其他模块的头文件中 */
extern bool Log_disconnections;
extern int	CommitDelay;
extern int	CommitSiblings;
extern char *default_tablespace;
extern char *temp_tablespaces;
extern bool ignore_checksum_failure;
extern bool ignore_invalid_pages;
extern bool synchronize_seqscans;
#ifdef FDDGM
extern bool g_fdballowChangeTdeEncryptType;
#endif //FDDGM

#ifdef TRACE_SYNCSCAN
extern bool trace_syncscan;
#endif
#ifdef DEBUG_BOUNDED_SORT
extern bool optimize_bounded_sort;
#endif

static int	GUC_check_errcode_value;

static List *reserved_class_prefix = NIL;

/* 检查钩子支持的全局变量 */
char	   *GUC_check_errmsg_string;
char	   *GUC_check_errdetail_string;
char	   *GUC_check_errhint_string;

static void fc_do_serialize(char **fc_destptr, Size *fc_maxbytes, const char *fc_fmt,...) pg_attribute_printf(3, 4);

static void set_config_sourcefile(const char *fc_name, char *fc_sourcefile,
								  int fc_sourceline);
static bool fc_call_bool_check_hook(struct config_bool *fc_conf, bool *fc_newval,
								 void **fc_extra, GucSource fc_source, int fc_elevel);
static bool fc_call_int_check_hook(struct config_int *fc_conf, int *fc_newval,
								void **fc_extra, GucSource fc_source, int fc_elevel);
static bool fc_call_real_check_hook(struct config_real *fc_conf, double *fc_newval,
								 void **fc_extra, GucSource fc_source, int fc_elevel);
static bool fc_call_string_check_hook(struct config_string *fc_conf, char **fc_newval,
								   void **fc_extra, GucSource fc_source, int fc_elevel);
static bool fc_call_enum_check_hook(struct config_enum *fc_conf, int *fc_newval,
								 void **fc_extra, GucSource fc_source, int fc_elevel);

static bool fc_check_log_destination(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_log_destination(const char *fc_newval, void *fc_extra);

static bool fc_check_wal_consistency_checking(char **fc_newval, void **fc_extra,
										   GucSource fc_source);
static void fc_assign_wal_consistency_checking(const char *fc_newval, void *fc_extra);

#ifdef HAVE_SYSLOG
static int	syslog_facility = LOG_LOCAL0;
#else
static int	syslog_facility = 0;
#endif

static void fc_assign_syslog_facility(int fc_newval, void *fc_extra);
static void fc_assign_syslog_ident(const char *fc_newval, void *fc_extra);
static void fc_assign_session_replication_role(int fc_newval, void *fc_extra);
static bool fc_check_temp_buffers(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_bonjour(bool *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_ssl(bool *fc_newval, void **fc_extra, GucSource fc_source);
#ifdef FDDGM
static bool fc_check_tlcp(bool *fc_newval, void **fc_extra, GucSource fc_source);
#endif
static bool fc_check_stage_log_stats(bool *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_log_stats(bool *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_canonical_path(char **fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_timezone_abbreviations(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_timezone_abbreviations(const char *fc_newval, void *fc_extra);
static void pg_timezone_abbrev_initialize(void);
static const char *fc_show_archive_command(void);
static void fc_assign_tcp_keepalives_idle(int fc_newval, void *fc_extra);
static void fc_assign_tcp_keepalives_interval(int fc_newval, void *fc_extra);
static void fc_assign_tcp_keepalives_count(int fc_newval, void *fc_extra);
static void fc_assign_tcp_user_timeout(int fc_newval, void *fc_extra);
static const char *fc_show_tcp_keepalives_idle(void);
static const char *fc_show_tcp_keepalives_interval(void);
static const char *fc_show_tcp_keepalives_count(void);
static const char *fc_show_tcp_user_timeout(void);
static bool fc_check_maxconnections(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_max_worker_processes(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_autovacuum_max_workers(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_max_wal_senders(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_autovacuum_work_mem(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_effective_io_concurrency(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_maintenance_io_concurrency(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_huge_page_size(int *fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_client_connection_check_interval(int *fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_maintenance_io_concurrency(int fc_newval, void *fc_extra);
static bool fc_check_application_name(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_application_name(const char *fc_newval, void *fc_extra);
static bool fc_check_cluster_name(char **fc_newval, void **fc_extra, GucSource fc_source);
static const char *fc_show_unix_socket_permissions(void);
static const char *fc_show_log_file_mode(void);
static const char *fc_show_data_directory_mode(void);
static const char *fc_show_in_hot_standby(void);
static bool fc_check_backtrace_functions(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_backtrace_functions(const char *fc_newval, void *fc_extra);
static bool fc_check_recovery_target_timeline(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_recovery_target_timeline(const char *fc_newval, void *fc_extra);
static bool fc_check_recovery_target(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_recovery_target(const char *fc_newval, void *fc_extra);
static bool fc_check_recovery_target_xid(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_recovery_target_xid(const char *fc_newval, void *fc_extra);
static bool fc_check_recovery_target_time(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_recovery_target_time(const char *fc_newval, void *fc_extra);
static bool fc_check_recovery_target_name(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_recovery_target_name(const char *fc_newval, void *fc_extra);
static bool fc_check_recovery_target_lsn(char **fc_newval, void **fc_extra, GucSource fc_source);
static void fc_assign_recovery_target_lsn(const char *fc_newval, void *fc_extra);
static bool fc_check_primary_slot_name(char **fc_newval, void **fc_extra, GucSource fc_source);
static bool fc_check_default_with_oids(bool *fc_newval, void **fc_extra, GucSource fc_source);

/* guc-file.l中的私有函数，需要从guc.c调用 */
static ConfigVariable *ProcessConfigFileInternal(GucContext fc_context,
												 bool fc_applySettings, int fc_elevel);

/*
 * 跟踪wal_consistency_checking中指定的自定义资源管理器是否有任何延迟检查。
 */
static bool check_wal_consistency_checking_deferred = false;

/*
 * 本模块中定义的枚举值的选项。
 *
 * 注意！选项值可能不包含双引号！
 */

static const struct config_enum_entry bytea_output_options[] = {
	{"escape", BYTEA_OUTPUT_ESCAPE, false},
	{"hex", BYTEA_OUTPUT_HEX, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(bytea_output_options) == (BYTEA_OUTPUT_HEX + 2),
				 "array length mismatch");

/*
 * 客户端和服务器消息级别选项的集合不同，因为它们的排序略有不同（见“log”级别），
 * 并且因为“fatal”/“panic”对client_min_messages没有意义。
 */
static const struct config_enum_entry client_message_level_options[] = {
	{"debug5", DEBUG5, false},
	{"debug4", DEBUG4, false},
	{"debug3", DEBUG3, false},
	{"debug2", DEBUG2, false},
	{"debug1", DEBUG1, false},
	{"debug", DEBUG2, true},
	{"log", LOG, false},
	{"info", INFO, true},
	{"notice", NOTICE, false},
	{"warning", WARNING, false},
	{"error", ERROR, false},
	{NULL, 0, false}
};

static const struct config_enum_entry server_message_level_options[] = {
	{"debug5", DEBUG5, false},
	{"debug4", DEBUG4, false},
	{"debug3", DEBUG3, false},
	{"debug2", DEBUG2, false},
	{"debug1", DEBUG1, false},
	{"debug", DEBUG2, true},
	{"info", INFO, false},
	{"notice", NOTICE, false},
	{"warning", WARNING, false},
	{"error", ERROR, false},
	{"log", LOG, false},
	{"fatal", FATAL, false},
	{"panic", PANIC, false},
	{NULL, 0, false}
};

static const struct config_enum_entry intervalstyle_options[] = {
	{"postgres", INTSTYLE_POSTGRES, false},
	{"postgres_verbose", INTSTYLE_POSTGRES_VERBOSE, false},
	{"sql_standard", INTSTYLE_SQL_STANDARD, false},
	{"iso_8601", INTSTYLE_ISO_8601, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(intervalstyle_options) == (INTSTYLE_ISO_8601 + 2),
				 "array length mismatch");

static const struct config_enum_entry log_error_verbosity_options[] = {
	{"terse", PGERROR_TERSE, false},
	{"default", PGERROR_DEFAULT, false},
	{"verbose", PGERROR_VERBOSE, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(log_error_verbosity_options) == (PGERROR_VERBOSE + 2),
				 "array length mismatch");

static const struct config_enum_entry log_statement_options[] = {
	{"none", LOGSTMT_NONE, false},
	{"ddl", LOGSTMT_DDL, false},
	{"mod", LOGSTMT_MOD, false},
	{"all", LOGSTMT_ALL, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(log_statement_options) == (LOGSTMT_ALL + 2),
				 "array length mismatch");

static const struct config_enum_entry isolation_level_options[] = {
	{"serializable", XACT_SERIALIZABLE, false},
	{"repeatable read", XACT_REPEATABLE_READ, false},
	{"read committed", XACT_READ_COMMITTED, false},
	{"read uncommitted", XACT_READ_UNCOMMITTED, false},
	{NULL, 0}
};

static const struct config_enum_entry session_replication_role_options[] = {
	{"origin", SESSION_REPLICATION_ROLE_ORIGIN, false},
	{"replica", SESSION_REPLICATION_ROLE_REPLICA, false},
	{"local", SESSION_REPLICATION_ROLE_LOCAL, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(session_replication_role_options) == (SESSION_REPLICATION_ROLE_LOCAL + 2),
				 "array length mismatch");

static const struct config_enum_entry syslog_facility_options[] = {
#ifdef HAVE_SYSLOG
	{"local0", LOG_LOCAL0, false},
	{"local1", LOG_LOCAL1, false},
	{"local2", LOG_LOCAL2, false},
	{"local3", LOG_LOCAL3, false},
	{"local4", LOG_LOCAL4, false},
	{"local5", LOG_LOCAL5, false},
	{"local6", LOG_LOCAL6, false},
	{"local7", LOG_LOCAL7, false},
#else
	{"none", 0, false},
#endif
	{NULL, 0}
};

static const struct config_enum_entry track_function_options[] = {
	{"none", TRACK_FUNC_OFF, false},
	{"pl", TRACK_FUNC_PL, false},
	{"all", TRACK_FUNC_ALL, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(track_function_options) == (TRACK_FUNC_ALL + 2),
				 "array length mismatch");

static const struct config_enum_entry stats_fetch_consistency[] = {
	{"none", PGSTAT_FETCH_CONSISTENCY_NONE, false},
	{"cache", PGSTAT_FETCH_CONSISTENCY_CACHE, false},
	{"snapshot", PGSTAT_FETCH_CONSISTENCY_SNAPSHOT, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(stats_fetch_consistency) == (PGSTAT_FETCH_CONSISTENCY_SNAPSHOT + 2),
				 "array length mismatch");

static const struct config_enum_entry xmlbinary_options[] = {
	{"base64", XMLBINARY_BASE64, false},
	{"hex", XMLBINARY_HEX, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(xmlbinary_options) == (XMLBINARY_HEX + 2),
				 "array length mismatch");

static const struct config_enum_entry xmloption_options[] = {
	{"content", XMLOPTION_CONTENT, false},
	{"document", XMLOPTION_DOCUMENT, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(xmloption_options) == (XMLOPTION_CONTENT + 2),
				 "array length mismatch");

/*
 * 尽管只有“on”、“off”和“safe_encoding”被记录，但我们
 * 接受所有可能的“on”和“off”的变体。
 */
static const struct config_enum_entry backslash_quote_options[] = {
	{"safe_encoding", BACKSLASH_QUOTE_SAFE_ENCODING, false},
	{"on", BACKSLASH_QUOTE_ON, false},
	{"off", BACKSLASH_QUOTE_OFF, false},
	{"true", BACKSLASH_QUOTE_ON, true},
	{"false", BACKSLASH_QUOTE_OFF, true},
	{"yes", BACKSLASH_QUOTE_ON, true},
	{"no", BACKSLASH_QUOTE_OFF, true},
	{"1", BACKSLASH_QUOTE_ON, true},
	{"0", BACKSLASH_QUOTE_OFF, true},
	{NULL, 0, false}
};

/*
 * 尽管只有“on”、“off”和“auto”被记录，但我们接受
 * 所有可能的“on”和“off”的变体。
 */
static const struct config_enum_entry compute_query_id_options[] = {
	{"auto", COMPUTE_QUERY_ID_AUTO, false},
	{"regress", COMPUTE_QUERY_ID_REGRESS, false},
	{"on", COMPUTE_QUERY_ID_ON, false},
	{"off", COMPUTE_QUERY_ID_OFF, false},
	{"true", COMPUTE_QUERY_ID_ON, true},
	{"false", COMPUTE_QUERY_ID_OFF, true},
	{"yes", COMPUTE_QUERY_ID_ON, true},
	{"no", COMPUTE_QUERY_ID_OFF, true},
	{"1", COMPUTE_QUERY_ID_ON, true},
	{"0", COMPUTE_QUERY_ID_OFF, true},
	{NULL, 0, false}
};

/*
 * 尽管只有“on”、“off”和“partition”被记录，但我们接受
 * 所有可能的“on”和“off”的变体。
 */
static const struct config_enum_entry constraint_exclusion_options[] = {
	{"partition", CONSTRAINT_EXCLUSION_PARTITION, false},
	{"on", CONSTRAINT_EXCLUSION_ON, false},
	{"off", CONSTRAINT_EXCLUSION_OFF, false},
	{"true", CONSTRAINT_EXCLUSION_ON, true},
	{"false", CONSTRAINT_EXCLUSION_OFF, true},
	{"yes", CONSTRAINT_EXCLUSION_ON, true},
	{"no", CONSTRAINT_EXCLUSION_OFF, true},
	{"1", CONSTRAINT_EXCLUSION_ON, true},
	{"0", CONSTRAINT_EXCLUSION_OFF, true},
	{NULL, 0, false}
};

/*
 * 尽管只有“on”、“off”、“remote_apply”、“remote_write”和“local”被
 * 记录，但我们接受所有可能的“on”和“off”的变体。
 */
static const struct config_enum_entry synchronous_commit_options[] = {
	{"local", SYNCHRONOUS_COMMIT_LOCAL_FLUSH, false},
	{"remote_write", SYNCHRONOUS_COMMIT_REMOTE_WRITE, false},
	{"remote_apply", SYNCHRONOUS_COMMIT_REMOTE_APPLY, false},
	{"on", SYNCHRONOUS_COMMIT_ON, false},
	{"off", SYNCHRONOUS_COMMIT_OFF, false},
	{"true", SYNCHRONOUS_COMMIT_ON, true},
	{"false", SYNCHRONOUS_COMMIT_OFF, true},
	{"yes", SYNCHRONOUS_COMMIT_ON, true},
	{"no", SYNCHRONOUS_COMMIT_OFF, true},
	{"1", SYNCHRONOUS_COMMIT_ON, true},
	{"0", SYNCHRONOUS_COMMIT_OFF, true},
	{NULL, 0, false}
};

/*
 * 尽管只有“on”、“off”、“try”被记录，但我们接受所有可能的
 * “on”和“off”的变体。
 */
static const struct config_enum_entry huge_pages_options[] = {
	{"off", HUGE_PAGES_OFF, false},
	{"on", HUGE_PAGES_ON, false},
	{"try", HUGE_PAGES_TRY, false},
	{"true", HUGE_PAGES_ON, true},
	{"false", HUGE_PAGES_OFF, true},
	{"yes", HUGE_PAGES_ON, true},
	{"no", HUGE_PAGES_OFF, true},
	{"1", HUGE_PAGES_ON, true},
	{"0", HUGE_PAGES_OFF, true},
	{NULL, 0, false}
};

static const struct config_enum_entry recovery_prefetch_options[] = {
	{"off", RECOVERY_PREFETCH_OFF, false},
	{"on", RECOVERY_PREFETCH_ON, false},
	{"try", RECOVERY_PREFETCH_TRY, false},
	{"true", RECOVERY_PREFETCH_ON, true},
	{"false", RECOVERY_PREFETCH_OFF, true},
	{"yes", RECOVERY_PREFETCH_ON, true},
	{"no", RECOVERY_PREFETCH_OFF, true},
	{"1", RECOVERY_PREFETCH_ON, true},
	{"0", RECOVERY_PREFETCH_OFF, true},
	{NULL, 0, false}
};

static const struct config_enum_entry force_parallel_mode_options[] = {
	{"off", FORCE_PARALLEL_OFF, false},
	{"on", FORCE_PARALLEL_ON, false},
	{"regress", FORCE_PARALLEL_REGRESS, false},
	{"true", FORCE_PARALLEL_ON, true},
	{"false", FORCE_PARALLEL_OFF, true},
	{"yes", FORCE_PARALLEL_ON, true},
	{"no", FORCE_PARALLEL_OFF, true},
	{"1", FORCE_PARALLEL_ON, true},
	{"0", FORCE_PARALLEL_OFF, true},
	{NULL, 0, false}
};

static const struct config_enum_entry plan_cache_mode_options[] = {
	{"auto", PLAN_CACHE_MODE_AUTO, false},
	{"force_generic_plan", PLAN_CACHE_MODE_FORCE_GENERIC_PLAN, false},
	{"force_custom_plan", PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN, false},
	{NULL, 0, false}
};

static const struct config_enum_entry password_encryption_options[] = {
	{"md5", PASSWORD_TYPE_MD5, false},
#ifdef FDDGM
	{"sm3", PASSWORD_TYPE_SM3, false},
#endif
	{"scram-sha-256", PASSWORD_TYPE_SCRAM_SHA_256, false},
	{NULL, 0, false}
};

const struct config_enum_entry ssl_protocol_versions_info[] = {
	{"", PG_TLS_ANY, false},
	{"TLSv1", PG_TLS1_VERSION, false},
	{"TLSv1.1", PG_TLS1_1_VERSION, false},
	{"TLSv1.2", PG_TLS1_2_VERSION, false},
	{"TLSv1.3", PG_TLS1_3_VERSION, false},
	{NULL, 0, false}
};

StaticAssertDecl(lengthof(ssl_protocol_versions_info) == (PG_TLS1_3_VERSION + 2),
				 "array length mismatch");

static struct config_enum_entry recovery_init_sync_method_options[] = {
	{"fsync", RECOVERY_INIT_SYNC_METHOD_FSYNC, false},
#ifdef HAVE_SYNCFS
	{"syncfs", RECOVERY_INIT_SYNC_METHOD_SYNCFS, false},
#endif
	{NULL, 0, false}
};

static struct config_enum_entry shared_memory_options[] = {
#ifndef WIN32
	{"sysv", SHMEM_TYPE_SYSV, false},
#endif
#ifndef EXEC_BACKEND
	{"mmap", SHMEM_TYPE_MMAP, false},
#endif
#ifdef WIN32
	{"windows", SHMEM_TYPE_WINDOWS, false},
#endif
	{NULL, 0, false}
};

static struct config_enum_entry default_toast_compression_options[] = {
	{"pglz", TOAST_PGLZ_COMPRESSION, false},
#ifdef  USE_LZ4
	{"lz4", TOAST_LZ4_COMPRESSION, false},
#endif
	{NULL, 0, false}
};

static const struct config_enum_entry wal_compression_options[] = {
	{"pglz", WAL_COMPRESSION_PGLZ, false},
#ifdef USE_LZ4
	{"lz4", WAL_COMPRESSION_LZ4, false},
#endif
#ifdef USE_ZSTD
	{"zstd", WAL_COMPRESSION_ZSTD, false},
#endif
	{"on", WAL_COMPRESSION_PGLZ, false},
	{"off", WAL_COMPRESSION_NONE, false},
	{"true", WAL_COMPRESSION_PGLZ, true},
	{"false", WAL_COMPRESSION_NONE, true},
	{"yes", WAL_COMPRESSION_PGLZ, true},
	{"no", WAL_COMPRESSION_NONE, true},
	{"1", WAL_COMPRESSION_PGLZ, true},
	{"0", WAL_COMPRESSION_NONE, true},
	{NULL, 0, false}
};

#ifdef FDD
static struct config_enum_entry tde_encrypt_method_options[] = {
	{"plain", FDB_ENCRYPT_TYPE_NONE, false},
	{"rc4", FDB_ENCRYPT_TYPE_RC4, false},
#ifdef FDDGM
	{"sm4", FDB_ENCRYPT_TYPE_SM4, false},
#endif
	{NULL, 0, false}
};

#endif

/*
 * 存储在其他模块中的枚举值的选项
 */
extern const struct config_enum_entry wal_level_options[];
extern const struct config_enum_entry archive_mode_options[];
extern const struct config_enum_entry recovery_target_action_options[];
extern const struct config_enum_entry sync_method_options[];
extern const struct config_enum_entry dynamic_shared_memory_options[];

/*
 * 从该模块导出的GUC选项变量
 */
bool		log_duration = false;
bool		Debug_print_plan = false;
bool		Debug_print_parse = false;
bool		Debug_print_rewritten = false;
bool		Debug_pretty_print = true;

bool		log_parser_stats = false;
bool		log_planner_stats = false;
bool		log_executor_stats = false;
bool		log_statement_stats = false;	/* 这有点包罗上述三者 */
bool		log_btree_build_stats = false;
char	   *event_source;

bool		row_security;
bool		check_function_bodies = true;

/*
 * 此GUC完全存在于向后兼容性中，检查其定义以获取详细信息。
 */
bool		default_with_oids = false;
bool		session_auth_is_superuser;

int			log_min_error_statement = ERROR;
int			log_min_messages = WARNING;
int			client_min_messages = NOTICE;
int			log_min_duration_sample = -1;
int			log_min_duration_statement = -1;
int			log_parameter_max_length = -1;
int			log_parameter_max_length_on_error = 0;
int			log_temp_files = -1;
double		log_statement_sample_rate = 1.0;
double		log_xact_sample_rate = 0;
int			trace_recovery_messages = LOG;
char	   *backtrace_functions;
char	   *backtrace_symbol_list;

int			temp_file_limit = -1;

int			num_temp_buffers = 1024;

char	   *cluster_name = "";
char	   *ConfigFileName;
char	   *HbaFileName;
char	   *IdentFileName;
char	   *external_pid_file;

char	   *pgstat_temp_directory;

char	   *application_name;

int			tcp_keepalives_idle;
int			tcp_keepalives_interval;
int			tcp_keepalives_count;
int			tcp_user_timeout;

/*
 * SSL重新协商在PostgreSQL 9.5中被移除，但我们允许它
 * 设置为零（意味着从不重新协商），以保持向后兼容性。
 * 这样可以避免与从未支持重新协商的客户端断开兼容性，
 * 因此它们总是尝试将其置为零。
 */
int			ssl_renegotiation_limit;

/*
 * 这实际上应该在pg_shmem.c中，但在此处定义，以便不需要
 * 在pg_shmem.c的所有不同实现中重复。
 */
int			huge_pages;
int			huge_page_size;

/*
 * 这些变量都是虚拟变量，不做任何操作，除了在某些
 * 情况下提供SHOW以显示的值。真实状态在其他地方
 * 存在并由assign_hooks保持同步。
 */
static char *syslog_ident_str;
static double phony_random_seed;
static char *client_encoding_string;
static char *datestyle_string;
static char *locale_collate;
static char *locale_ctype;
static char *server_encoding_string;
static char *server_version_string;
static int	server_version_num;
static char *timezone_string;
static char *log_timezone_string;
static char *timezone_abbreviations_string;
static char *data_directory;
static char *session_authorization_string;
static int	max_function_args;
static int	max_index_keys;
static int	max_identifier_length;
static int	block_size;
static int	segment_size;
static int	shared_memory_size_mb;
static int	shared_memory_size_in_huge_pages;
static int	wal_block_size;
static bool data_checksums;
static bool integer_datetimes;
static bool assert_enabled;
static bool in_hot_standby;
static char *recovery_target_timeline_string;
static char *recovery_target_string;
static char *recovery_target_xid_string;
static char *recovery_target_name_string;
static char *recovery_target_lsn_string;
static char *restrict_nonsystem_relation_kind_string;


/* 应该是静态的，但commands/variable.c需要访问此内容 */
char	   *role_string;


/*
 * 上下文类型的可显示名称（枚举GucContext）
 *
 * 注意：这些字符串故意未进行本地化。
 */
const char *const GucContext_Names[] =
{
	 /* PGC_INTERNAL */ "internal",
	 /* PGC_POSTMASTER */ "postmaster",
	 /* PGC_SIGHUP */ "sighup",
	 /* PGC_SU_BACKEND */ "superuser-backend",
	 /* PGC_BACKEND */ "backend",
	 /* PGC_SUSET */ "superuser",
	 /* PGC_USERSET */ "user"
};

StaticAssertDecl(lengthof(GucContext_Names) == (PGC_USERSET + 1),
				 "array length mismatch");

/*
 * 源类型的可显示名称（枚举GucSource）
 *
 * 注意：这些字符串故意未进行本地化。
 */
const char *const GucSource_Names[] =
{
	 /* PGC_S_DEFAULT */ "default",
	 /* PGC_S_DYNAMIC_DEFAULT */ "default",
	 /* PGC_S_ENV_VAR */ "environment variable",
	 /* PGC_S_FILE */ "configuration file",
	 /* PGC_S_ARGV */
/*
 * GUC 表的内容
 *
 * 请参见 src/backend/utils/misc/README 以获取设计说明。
 *
 * 添加选项的方法：
 *
 * 1. 声明一个 bool、int、double 或 char* 类型的全局变量，并使用它。
 *
 * 2. 决定在什么情况下可以安全地设置选项。详见 guc.h。
 *
 * 3. 确定名称、默认值、上限和下限（如果适用）等。
 *
 * 4. 在下面添加记录。
 *
 * 5. 在 src/backend/utils/misc/postgresql.conf.sample 中添加它（如果适用）。
 *
 * 6. 不要忘记记录选项（至少在 config.sgml 中）。
 *
 * 7. 如果这是一个新的 GUC_LIST_QUOTE 选项，你必须将其添加到 
 *	  src/bin/pg_dump/dumputils.c 的 variable_is_guc_list_quote() 中。
 */
/******** 选项记录跟随 ********/
/* 不供一般使用 --- 由 SET SESSION AUTHORIZATION 使用 */
/*
	 * WITH OIDS 支持以及相应的 default_with_oids 在 PostgreSQL 12 中被移除，
	 * 但我们允许将参数设置为 false，以避免不必要地破坏较旧的转储文件。
	 */
/* 这未被记录，因为在标准构建中未暴露 */
/* 这未被记录，因为在标准构建中未暴露 */
/*
		 * 这不能保证可用，但考虑到这是开发者导向的选项，
		 * 增加检查可用性的代码似乎没有必要。
		 */
/*
		 * 这不能保证可用，但考虑到这是开发者导向的选项，
		 * 增加检查可用性的代码似乎没有必要。
		 */
/* 列表结束标记 */
/*
 * 为枚举 config_group 中定义的分组显示的名称
 */
/* 见 max_connections */
/* 文件位置 */
/* 连接认证设置 */
/* 连接认证认证 */
/* 连接认证SSL */
/* 资源内存 */
/* 资源磁盘 */
/* 资源内核 */
/* 资源清理延迟 */
/* 资源后台写入器 */
/* 资源异步 */
/* WAL设置 */
/* WAL检查点 */
/* WAL归档 */
/* WAL恢复 */
/* WAL归档恢复 */
/* WAL恢复目标 */
/* 复制发送 */
/* 复制主节点 */
/* 复制备用节点 */
/* 复制订阅者 */
/* 查询调优方法 */
/* 查询调优成本 */
/* 查询调优GEQO */
/* 查询调优其他 */
/* 日志记录位置 */
/* 日志记录时间 */
/* 日志记录内容 */
/* 进程标题 */
/* 统计监控 */
/* 统计累积 */
/* 自动清理 */
/* 客户端连接语句 */
/* 客户端连接区域设置 */
/* 客户端连接预加载 */
/* 客户端连接其他 */
/* 锁管理 */
/* 兼容选项_之前 */
/* 兼容选项_客户端 */
/* 错误处理 */
/* 预设选项 */
/* 自定义选项 */
/* 开发者选项 */
/* help_config 希望这个数组为空结束 */
/*
 * GUC变量类型的可显示名称（枚举 config_type）
 *
 * 注意：这些字符串故意不进行本地化。
 */
/* 执行赋值 */
/* 如果旧值不为 NULL 且不再被引用则释放旧值 */
/*
 * 检测 "extra" 结构是否在 GUC 项中的任何地方被引用
 */
/*
 * 支持将值赋给 GUC 项的 "extra" 字段。如果先前的值不在该项的其他地方被引用（包括堆栈状态），则释放该值。
 */
/* 执行赋值 */
/*
 * 单位转换表。
 *
 * 有两个表，一个用于内存单位，另一个用于时间单位。
 * 对于每种从一种单位到另一种单位的支持转换，
 * 我们在表中都有一个条目。
 *
 * 为了简单起见，并避免可能的舍入错误，
 * 转换从不连锁。所有单位（同一类型）之间必须有直接转换。
 *
 * 每种基本单位的转换必须按最大到最小的人类友好单位的顺序保持；
 * convert_xxx_from_base_unit()依赖于此。
 * （基本单位组的顺序无关紧要。）
 */
/* 识别的最长单位字符串的长度 */
/* 单位，作为字符串，例如 "kB" 或
 * "min" */
/* 无需执行任何操作 */
/* 单位转基本单位的转换因子 */
/* 确保表中的常量不会溢出或下溢 */
/* 表的结束标记 */
/* 表的结束标记 */ "command line",
	 /*
 * GUC 表的内容
 *
 * 请参见 src/backend/utils/misc/README 以获取设计说明。
 *
 * 添加选项的方法：
 *
 * 1. 声明一个 bool、int、double 或 char* 类型的全局变量，并使用它。
 *
 * 2. 决定在什么情况下可以安全地设置选项。详见 guc.h。
 *
 * 3. 确定名称、默认值、上限和下限（如果适用）等。
 *
 * 4. 在下面添加记录。
 *
 * 5. 在 src/backend/utils/misc/postgresql.conf.sample 中添加它（如果适用）。
 *
 * 6. 不要忘记记录选项（至少在 config.sgml 中）。
 *
 * 7. 如果这是一个新的 GUC_LIST_QUOTE 选项，你必须将其添加到 
 *	  src/bin/pg_dump/dumputils.c 的 variable_is_guc_list_quote() 中。
 */ "global",
	 /******** 选项记录跟随 ********/ "database",
	 /* 不供一般使用 --- 由 SET SESSION AUTHORIZATION 使用 */ "user",
	 /*
	 * WITH OIDS 支持以及相应的 default_with_oids 在 PostgreSQL 12 中被移除，
	 * 但我们允许将参数设置为 false，以避免不必要地破坏较旧的转储文件。
	 */ "database user",
	 /* 这未被记录，因为在标准构建中未暴露 */ "client",
	 /* 这未被记录，因为在标准构建中未暴露 */ "override",
	 /*
		 * 这不能保证可用，但考虑到这是开发者导向的选项，
		 * 增加检查可用性的代码似乎没有必要。
		 */ "interactive",
	 /*
		 * 这不能保证可用，但考虑到这是开发者导向的选项，
		 * 增加检查可用性的代码似乎没有必要。
		 */ "test",
	 /* 列表结束标记 */ "session"
};

StaticAssertDecl(lengthof(GucSource_Names) == (PGC_S_SESSION + 1),
				 "array length mismatch");

/* 这是 PGC_SUSET，以防止在 log_lock_waits 中隐藏。 */
const char *const config_group_names[] =
{
	/* 见 max_connections */
	gettext_noop("Ungrouped"),
	/*
	 * 我们有时将共享缓冲区的数量乘以二而不检查溢出，
	 * 所以我们不应允许超过 INT_MAX / 2。
	 */
	gettext_noop("File Locations"),
	/*
	 * 我们使用希望安全的较小值 100kB 作为 max_stack_depth 的编译默认值。
	 * InitializeGUCOptions 将在可能的情况下增加它，具体取决于实际的
	 * 平台特定堆栈限制。
	 */
	gettext_noop("Connections and Authentication / Connection Settings"),
	/*
	 * 如果这个参数发生变化，请参见 CheckRequiredParameterValues()
	 */
	gettext_noop("Connections and Authentication / Authentication"),
	/* 见 ComputeXidHorizons */
	gettext_noop("Connections and Authentication / SSL"),
	/*
	 * 如果这个参数发生变化，请参见 CheckRequiredParameterValues()
	 */
	gettext_noop("Resource Usage / Memory"),
	/* 不供一般使用 */
	gettext_noop("Resource Usage / Disk"),
	/* 见 max_wal_senders */
	gettext_noop("Resource Usage / Kernel Resources"),
	/* 未知名称 */
	gettext_noop("Resource Usage / Cost-Based Vacuum Delay"),
	/* 我们没有微秒的指定，因此不能在这里提供单位 */
	gettext_noop("Resource Usage / Background Writer"),
	/* 与 shared_buffers 相同的上限 */
	gettext_noop("Resource Usage / Asynchronous Behavior"),
	/* 见 varsup.c 理解为什么这是 PGC_POSTMASTER 而不是 PGC_SIGHUP */
	gettext_noop("Write-Ahead Log / Settings"),
	/* 如果你更改上限值，请参见 vacuum_failsafe_age。 */
	gettext_noop("Write-Ahead Log / Checkpoints"),
	/* 见 multixact.c 理解为什么这是 PGC_POSTMASTER 而不是 PGC_SIGHUP */
	gettext_noop("Write-Ahead Log / Archiving"),
	/* 见 max_connections */
	gettext_noop("Write-Ahead Log / Recovery"),
	/* 不能在 postgresql.conf 中设置 */
	gettext_noop("Write-Ahead Log / Archive Recovery"),
	/* 根据旧的仅编译时缓存清除宏设置默认值 */
	gettext_noop("Write-Ahead Log / Recovery Target"),
	/*
 * 检查是否应允许为给定名称创建pg_parameter_acl条目。
 * （这可以在规范化之前或之后应用。）
 */
	gettext_noop("Replication / Sending Servers"),
	/*
 * 检查是否应允许为给定名称创建pg_parameter_acl条目。
 * （这可以在规范化之前或之后应用。）
 */
	gettext_noop("Replication / Primary Server"),
	/* 列表结束标记 */
	gettext_noop("Replication / Standby Servers"),
	/* 列表结束标记 */
	gettext_noop("Replication / Subscribers"),
	/* 请查看 main.c，了解为什么 LC_foo 的默认值并不相同 */
	gettext_noop("Query Tuning / Planner Method Configuration"),
	/* 不能在 postgresql.conf 中设置 */
	gettext_noop("Query Tuning / Planner Cost Constants"),
	/* 不能在 postgresql.conf 中设置 */
	gettext_noop("Query Tuning / Genetic Query Optimizer"),
	/* 不供一般使用 --- 由 SET ROLE 使用 */
	gettext_noop("Query Tuning / Other Planner Options"),
	/* 不供一般使用 --- 由 SET SESSION AUTHORIZATION 使用 */
	gettext_noop("Reporting and Logging / Where to Log"),
	/*
		 * 不能通过 ALTER SYSTEM 设置，因为这可能导致 data_directory 的递归定义。
		 */
	gettext_noop("Reporting and Logging / When to Log"),
	/* 结束列表标记 */
	gettext_noop("Reporting and Logging / What to Log"),
	/*
		 * client_message_level_options 的值确实太多，但为此单独设置选项数组并不值得。
		 */
	gettext_noop("Reporting and Logging / Process Title"),
	/* 不允许 PG_TLS_ANY */
	gettext_noop("Statistics / Monitoring"),
	/* 结束列表标记 */
	gettext_noop("Statistics / Cumulative Query and Index Statistics"),
	/******** 选项列表结束 ********/
	gettext_noop("Autovacuum"),
	/*
 * 为了继续支持过时的 GUC 变量名称，我们将以下映射应用于任何无法识别的名称。请注意，只有当新变量与旧变量具有非常相似的语义时，才应将旧名称映射到新名称。
 */
	gettext_noop("Client Connection Defaults / Statement Behavior"),
	/*
 * 实际的变量查找是通过这个单一的、已排序数组进行的。
 */
	gettext_noop("Client Connection Defaults / Locale and Formatting"),
	/* 向量中包含的当前变量数量 */
	gettext_noop("Client Connection Defaults / Shared Library Preloading"),
	/* 向量容量 */
	gettext_noop("Client Connection Defaults / Other Defaults"),
	/* 如果需要做提交/中止工作则为真 */
	gettext_noop("Lock Management"),
	/* 如果启用 GUC_REPORT 则为真 */
	gettext_noop("Version and Platform Compatibility / Previous PostgreSQL Versions"),
	/* 如果需要任何 GUC_REPORT 报告则为真 */
	gettext_noop("Version and Platform Compatibility / Other Platforms and Clients"),
	/* 当在主事务中时为 1 */
	gettext_noop("Error Handling"),
	/*
 * 检查 malloc/strdup/realloc 调用的一些基础设施
 */
	gettext_noop("Preset Options"),
	/* 避免 malloc(0) 的不可移植行为 */
	gettext_noop("Customized Options"),
	/* 避免 realloc(NULL, 0) 的不可移植行为 */
	gettext_noop("Developer Options"),
	/*
 * 检测 strval 是否在 GUC 字符串项中的任何地方被引用
 */
	NULL
};

StaticAssertDecl(lengthof(config_group_names) == (DEVELOPER_OPTIONS + 2),
				 "array length mismatch");

/*
 * 支持将值赋给字符串 GUC 项的一个字段。如果先前的值不在该项的其他地方被引用（包括堆栈状态），则释放该值。
 */
const char *const config_type_names[] =
{
	 /* 执行赋值 */ "bool",
	 /* 如果旧值不为 NULL 且不再被引用则释放旧值 */ "integer",
	 /*
 * 检测 "extra" 结构是否在 GUC 项中的任何地方被引用
 */ "real",
	 /*
 * 支持将值赋给 GUC 项的 "extra" 字段。如果先前的值不在该项的其他地方被引用（包括堆栈状态），则释放该值。
 */ "string",
	 /* 执行赋值 */ "enum"
};

StaticAssertDecl(lengthof(config_type_names) == (PGC_ENUM + 1),
				 "array length mismatch");

/* 如果旧值不为 NULL 且不再被引用则释放旧值 */
#define MAX_UNIT_LEN		3	/*
 * 支持将变量的活动值复制到堆栈条目中。
 * 与活动值关联的 "extra" 字段也会被复制。
 *
 * 注意：确保新堆栈条目的 stringval 和 extra 字段在使用之前被初始化为 NULL，否则我们将尝试释放它们。
 */

typedef struct
{
	char		unit[MAX_UNIT_LEN + 1]; /*
 * 支持丢弃堆栈条目中不再需要的值。
 * 与堆栈条目关联的 "extra" 字段也被清除。
 */
	int			base_unit;		/* 无需执行任何操作 */
	double		multiplier;		/*
 * 获取已排序数组指针（仅供 help_config.c 使用）
 */
} unit_conversion;

/*
 * 构建已排序数组。这个操作被拆分出来，以便可以
 * 在启动后重新执行（例如，我们可以允许可加载模块
 * 添加变量，然后我们需要重新排序）。
 */
#if BLCKSZ < 1024 || BLCKSZ > (1024*1024)
#error BLCKSZ must be between 1KB and 1MB
#endif
#if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
#error XLOG_BLCKSZ must be between 1KB and 1MB
#endif

static const char *memory_units_hint = gettext_noop("Valid units for this parameter are \"B\", \"kB\", \"MB\", \"GB\", and \"TB\".");

static const unit_conversion memory_unit_conversion_table[] =
{
	{"TB", GUC_UNIT_BYTE, 1024.0 * 1024.0 * 1024.0 * 1024.0},
	{"GB", GUC_UNIT_BYTE, 1024.0 * 1024.0 * 1024.0},
	{"MB", GUC_UNIT_BYTE, 1024.0 * 1024.0},
	{"kB", GUC_UNIT_BYTE, 1024.0},
	{"B", GUC_UNIT_BYTE, 1.0},

	{"TB", GUC_UNIT_KB, 1024.0 * 1024.0 * 1024.0},
	{"GB", GUC_UNIT_KB, 1024.0 * 1024.0},
	{"MB", GUC_UNIT_KB, 1024.0},
	{"kB", GUC_UNIT_KB, 1.0},
	{"B", GUC_UNIT_KB, 1.0 / 1024.0},

	{"TB", GUC_UNIT_MB, 1024.0 * 1024.0},
	{"GB", GUC_UNIT_MB, 1024.0},
	{"MB", GUC_UNIT_MB, 1.0},
	{"kB", GUC_UNIT_MB, 1.0 / 1024.0},
	{"B", GUC_UNIT_MB, 1.0 / (1024.0 * 1024.0)},

	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (BLCKSZ / 1024)},
	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (BLCKSZ / 1024)},
	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (BLCKSZ / 1024)},
	{"kB", GUC_UNIT_BLOCKS, 1.0 / (BLCKSZ / 1024)},
	{"B", GUC_UNIT_BLOCKS, 1.0 / BLCKSZ},

	{"TB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
	{"GB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
	{"MB", GUC_UNIT_XBLOCKS, 1024.0 / (XLOG_BLCKSZ / 1024)},
	{"kB", GUC_UNIT_XBLOCKS, 1.0 / (XLOG_BLCKSZ / 1024)},
	{"B", GUC_UNIT_XBLOCKS, 1.0 / XLOG_BLCKSZ},

	{""}						/* 与其要求手动填写 vartype，不如这样做： */
};

static const char *time_units_hint = gettext_noop("Valid units for this parameter are \"us\", \"ms\", \"s\", \"min\", \"h\", and \"d\".");

static const unit_conversion time_unit_conversion_table[] =
{
	{"d", GUC_UNIT_MS, 1000 * 60 * 60 * 24},
	{"h", GUC_UNIT_MS, 1000 * 60 * 60},
	{"min", GUC_UNIT_MS, 1000 * 60},
	{"s", GUC_UNIT_MS, 1000},
	{"ms", GUC_UNIT_MS, 1},
	{"us", GUC_UNIT_MS, 1.0 / 1000},

	{"d", GUC_UNIT_S, 60 * 60 * 24},
	{"h", GUC_UNIT_S, 60 * 60},
	{"min", GUC_UNIT_S, 60},
	{"s", GUC_UNIT_S, 1},
	{"ms", GUC_UNIT_S, 1.0 / 1000},
	{"us", GUC_UNIT_S, 1.0 / (1000 * 1000)},

	{"d", GUC_UNIT_MIN, 60 * 24},
	{"h", GUC_UNIT_MIN, 60},
	{"min", GUC_UNIT_MIN, 1},
	{"s", GUC_UNIT_MIN, 1.0 / 60},
	{"ms", GUC_UNIT_MIN, 1.0 / (1000 * 60)},
	{"us", GUC_UNIT_MIN, 1.0 / (1000 * 1000 * 60)},

	{""}						/* 与其要求手动填写 vartype，不如这样做： */
};

/*
 * 将新的GUC变量添加到已知变量列表中。如果需要，列表将扩展。
 */


#ifdef FDD
static const char * fc_FDB_showEncryptionKeyCommand(void)
{
	if (g_fdbEncryptionKeyCommand)
		return g_fdbEncryptionKeyCommand;
	else
		return "(disabled)";
}

static bool fc_FDB_checkTDEEncrpt(int *fc_new_value, void **fc_extra, GucSource fc_source)
{
	if(NULL == g_fdbEncryptionKeyCommand && FDB_ENCRYPT_TYPE_NONE != *fc_new_value)
	{
		GUC_check_errmsg("Can't enable transparent encryption, encryption_key_command not configure");
		return false;
	}

#ifdef FDDGM
	if(!g_fdballowChangeTdeEncryptType && g_tde_encrypt != *fc_new_value  && FDB_ENCRYPT_TYPE_NONE != g_tde_encrypt && FDB_ENCRYPT_TYPE_NONE != *fc_new_value)
#else
	if(g_tde_encrypt != *fc_new_value && FDB_ENCRYPT_TYPE_NONE != g_tde_encrypt && FDB_ENCRYPT_TYPE_NONE != *fc_new_value)
#endif
	{
		GUC_check_errmsg("cannot change TDE encryption algorithm,from %s to %s",fdb_encrypttype_tochar(g_tde_encrypt),fdb_encrypttype_tochar(*fc_new_value));
		return false;	
	}

	return true;
}

static void fc_FDB_assignTDEEncrpt(int fc_new_encrypt, void *fc_extra)
{
	if(NULL == g_fdbEncryptionKeyCommand && FDB_ENCRYPT_TYPE_NONE != fc_new_encrypt)
	{		
		//ereport(PANIC,(errcode(ERRCODE_WARNING), errmsg("Can't enable transparent encryption, encryption_key_command not configure")));
	}
}

#endif



/*
		 * 将向量增加25%
		 */

static struct config_bool ConfigureNamesBool[] =
{
	{
		{"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of sequential-scan plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_seqscan,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of index-scan plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_indexscan,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of index-only-scan plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_indexonlyscan,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of bitmap-scan plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_bitmapscan,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of TID scan plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_tidscan,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of explicit sort steps."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_sort,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_incremental_sort", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of incremental sort steps."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_incremental_sort,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of hashed aggregation plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_hashagg,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of materialization."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_material,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of memoization."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_memoize,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of nested-loop join plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_nestloop,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of merge join plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_mergejoin,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of hash join plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_hashjoin,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of gather merge plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_gathermerge,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables partitionwise join."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_partitionwise_join,
		false,
		NULL, NULL, NULL
	},
	{
		{"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables partitionwise aggregation and grouping."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_partitionwise_aggregate,
		false,
		NULL, NULL, NULL
	},
	{
		{"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of parallel append plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_parallel_append,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of parallel hash plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_parallel_hash,
		true,
		NULL, NULL, NULL
	},
#ifdef FDDQ	
	{
		{"enable_logical_replication", PGC_USERSET, REPLICATION_PRIMARY,
			gettext_noop("Close logical replication of transactions in the session."),
			NULL
		},
		&enable_logical_replication,
		true,
		NULL, NULL, NULL
	},
#endif

	{
		{"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables plan-time and execution-time partition pruning."),
			gettext_noop("Allows the query planner and executor to compare partition "
						 "bounds to conditions in the query to determine which "
						 "partitions must be scanned."),
			GUC_EXPLAIN
		},
		&enable_partition_pruning,
		true,
		NULL, NULL, NULL
	},
	{
		{"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enables the planner's use of async append plans."),
			NULL,
			GUC_EXPLAIN
		},
		&enable_async_append,
		true,
		NULL, NULL, NULL
	},
	{
		{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
			gettext_noop("Enables genetic query optimization."),
			gettext_noop("This algorithm attempts to do planning without "
						 "exhaustive searching."),
			GUC_EXPLAIN
		},
		&enable_geqo,
		true,
		NULL, NULL, NULL
	},
	{
		/* 内存不足 */
		{"is_superuser", PGC_INTERNAL, UNGROUPED,
			gettext_noop("Shows whether the current user is a superuser."),
			NULL,
			GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_ALLOW_IN_PARALLEL
		},
		&session_auth_is_superuser,
		false,
		NULL, NULL, NULL
	},
	{
		{"bonjour", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Enables advertising the server via Bonjour."),
			NULL
		},
		&enable_bonjour,
		false,
		fc_check_bonjour, NULL, NULL
	},
	{
		{"track_commit_timestamp", PGC_POSTMASTER, REPLICATION_SENDING,
			gettext_noop("Collects transaction commit time."),
			NULL
		},
		&track_commit_timestamp,
		false,
		NULL, NULL, NULL
	},
	{
		{"ssl", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Enables SSL connections."),
			NULL
		},
		&EnableSSL,
		false,
		fc_check_ssl, NULL, NULL
	},
#ifdef FDDGM
	{
		{"enable_tlcp", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Enables GM TLCP connections."),
			NULL,
			GUC_NO_SHOW_ALL
		},
		&EnableTLCP,
		false,
		fc_check_tlcp, NULL, NULL
	},
#endif

	{
		{"ssl_passphrase_command_supports_reload", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Controls whether ssl_passphrase_command is called during server reload."),
			NULL
		},
		&ssl_passphrase_command_supports_reload,
		false,
		NULL, NULL, NULL
	},
	{
		{"ssl_prefer_server_ciphers", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Give priority to server ciphersuite order."),
			NULL
		},
		&SSLPreferServerCiphers,
		true,
		NULL, NULL, NULL
	},
	{
		{"fsync", PGC_SIGHUP, WAL_SETTINGS,
			gettext_noop("Forces synchronization of updates to disk."),
			gettext_noop("The server will use the fsync() system call in several places to make "
						 "sure that updates are physically written to disk. This ensures "
						 "that a database cluster will recover to a consistent state after "
						 "an operating system or hardware crash.")
		},
		&enableFsync,
		true,
		NULL, NULL, NULL
	},
	{
		{"ignore_checksum_failure", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Continues processing after a checksum failure."),
			gettext_noop("Detection of a checksum failure normally causes PostgreSQL to "
						 "report an error, aborting the current transaction. Setting "
						 "ignore_checksum_failure to true causes the system to ignore the failure "
						 "(but still report a warning), and continue processing. This "
						 "behavior could cause crashes or other serious problems. Only "
						 "has an effect if checksums are enabled."),
			GUC_NOT_IN_SAMPLE
		},
		&ignore_checksum_failure,
		false,
		NULL, NULL, NULL
	},
	{
		{"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Continues processing past damaged page headers."),
			gettext_noop("Detection of a damaged page header normally causes PostgreSQL to "
						 "report an error, aborting the current transaction. Setting "
						 "zero_damaged_pages to true causes the system to instead report a "
						 "warning, zero out the damaged page, and continue processing. This "
						 "behavior will destroy data, namely all the rows on the damaged page."),
			GUC_NOT_IN_SAMPLE
		},
		&zero_damaged_pages,
		false,
		NULL, NULL, NULL
	},
	{
		{"ignore_invalid_pages", PGC_POSTMASTER, DEVELOPER_OPTIONS,
			gettext_noop("Continues recovery after an invalid pages failure."),
			gettext_noop("Detection of WAL records having references to "
						 "invalid pages during recovery causes PostgreSQL to "
						 "raise a PANIC-level error, aborting the recovery. "
						 "Setting ignore_invalid_pages to true causes "
						 "the system to ignore invalid page references "
						 "in WAL records (but still report a warning), "
						 "and continue recovery. This behavior may cause "
						 "crashes, data loss, propagate or hide corruption, "
						 "or other serious problems. Only has an effect "
						 "during recovery or in standby mode."),
			GUC_NOT_IN_SAMPLE
		},
		&ignore_invalid_pages,
		false,
		NULL, NULL, NULL
	},
	{
		{"full_page_writes", PGC_SIGHUP, WAL_SETTINGS,
			gettext_noop("Writes full pages to WAL when first modified after a checkpoint."),
			gettext_noop("A page write in process during an operating system crash might be "
						 "only partially written to disk.  During recovery, the row changes "
						 "stored in WAL are not enough to recover.  This option writes "
						 "pages when first modified after a checkpoint to WAL so full recovery "
						 "is possible.")
		},
		&fullPageWrites,
		true,
		NULL, NULL, NULL
	},

	{
		{"wal_log_hints", PGC_POSTMASTER, WAL_SETTINGS,
			gettext_noop("Writes full pages to WAL when first modified after a checkpoint, even for a non-critical modification."),
			NULL
		},
		&wal_log_hints,
		false,
		NULL, NULL, NULL
	},

	{
		{"wal_init_zero", PGC_SUSET, WAL_SETTINGS,
			gettext_noop("Writes zeroes to new WAL files before first use."),
			NULL
		},
		&wal_init_zero,
		true,
		NULL, NULL, NULL
	},

	{
		{"wal_recycle", PGC_SUSET, WAL_SETTINGS,
			gettext_noop("Recycles WAL files by renaming them."),
			NULL
		},
		&wal_recycle,
		true,
		NULL, NULL, NULL
	},

	{
		{"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT,
			gettext_noop("Logs each checkpoint."),
			NULL
		},
		&log_checkpoints,
		true,
		NULL, NULL, NULL
	},
	{
		{"log_connections", PGC_SU_BACKEND, LOGGING_WHAT,
			gettext_noop("Logs each successful connection."),
			NULL
		},
		&Log_connections,
		false,
		NULL, NULL, NULL
	},
	{
		{"log_disconnections", PGC_SU_BACKEND, LOGGING_WHAT,
			gettext_noop("Logs end of a session, including duration."),
			NULL
		},
		&Log_disconnections,
		false,
		NULL, NULL, NULL
	},
	{
		{"log_replication_commands", PGC_SUSET, LOGGING_WHAT,
			gettext_noop("Logs each replication command."),
			NULL
		},
		&log_replication_commands,
		false,
		NULL, NULL, NULL
	},
	{
		{"debug_assertions", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows whether the running server has assertion checks enabled."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&assert_enabled,
#ifdef USE_ASSERT_CHECKING
		true,
#else
		false,
#endif
		NULL, NULL, NULL
	},

	{
		{"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
			gettext_noop("Terminate session on any error."),
			NULL
		},
		&ExitOnAnyError,
		false,
		NULL, NULL, NULL
	},
	{
		{"restart_after_crash", PGC_SIGHUP, ERROR_HANDLING_OPTIONS,
			gettext_noop("Reinitialize server after backend crash."),
			NULL
		},
		&restart_after_crash,
		true,
		NULL, NULL, NULL
	},
	{
		{"remove_temp_files_after_crash", PGC_SIGHUP, DEVELOPER_OPTIONS,
			gettext_noop("Remove temporary files after backend crash."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&remove_temp_files_after_crash,
		true,
		NULL, NULL, NULL
	},

	{
		{"log_duration", PGC_SUSET, LOGGING_WHAT,
			gettext_noop("Logs the duration of each completed SQL statement."),
			NULL
		},
		&log_duration,
		false,
		NULL, NULL, NULL
	},
	{
		{"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
			gettext_noop("Logs each query's parse tree."),
			NULL
		},
		&Debug_print_parse,
		false,
		NULL, NULL, NULL
	},
	{
		{"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
			gettext_noop("Logs each query's rewritten parse tree."),
			NULL
		},
		&Debug_print_rewritten,
		false,
		NULL, NULL, NULL
	},
	{
		{"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
			gettext_noop("Logs each query's execution plan."),
			NULL
		},
		&Debug_print_plan,
		false,
		NULL, NULL, NULL
	},
	{
		{"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
			gettext_noop("Indents parse and plan tree displays."),
			NULL
		},
		&Debug_pretty_print,
		true,
		NULL, NULL, NULL
	},
	{
		{"log_parser_stats", PGC_SUSET, STATS_MONITORING,
			gettext_noop("Writes parser performance statistics to the server log."),
			NULL
		},
		&log_parser_stats,
		false,
		fc_check_stage_log_stats, NULL, NULL
	},
	{
		{"log_planner_stats", PGC_SUSET, STATS_MONITORING,
			gettext_noop("Writes planner performance statistics to the server log."),
			NULL
		},
		&log_planner_stats,
		false,
		fc_check_stage_log_stats, NULL, NULL
	},
	{
		{"log_executor_stats", PGC_SUSET, STATS_MONITORING,
			gettext_noop("Writes executor performance statistics to the server log."),
			NULL
		},
		&log_executor_stats,
		false,
		fc_check_stage_log_stats, NULL, NULL
	},
	{
		{"log_statement_stats", PGC_SUSET, STATS_MONITORING,
			gettext_noop("Writes cumulative performance statistics to the server log."),
			NULL
		},
		&log_statement_stats,
		false,
		fc_check_log_stats, NULL, NULL
	},
#ifdef BTREE_BUILD_STATS
	{
		{"log_btree_build_stats", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Logs system resource usage statistics (memory and CPU) on various B-tree operations."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&log_btree_build_stats,
		false,
		NULL, NULL, NULL
	},
#endif

	{
		{"track_activities", PGC_SUSET, STATS_CUMULATIVE,
			gettext_noop("Collects information about executing commands."),
			gettext_noop("Enables the collection of information on the currently "
						 "executing command of each session, along with "
						 "the time at which that command began execution.")
		},
		&pgstat_track_activities,
		true,
		NULL, NULL, NULL
	},
	{
		{"track_counts", PGC_SUSET, STATS_CUMULATIVE,
			gettext_noop("Collects statistics on database activity."),
			NULL
		},
		&pgstat_track_counts,
		true,
		NULL, NULL, NULL
	},
	{
		{"track_io_timing", PGC_SUSET, STATS_CUMULATIVE,
			gettext_noop("Collects timing statistics for database I/O activity."),
			NULL
		},
		&track_io_timing,
		false,
		NULL, NULL, NULL
	},
	{
		{"track_wal_io_timing", PGC_SUSET, STATS_CUMULATIVE,
			gettext_noop("Collects timing statistics for WAL I/O activity."),
			NULL
		},
		&track_wal_io_timing,
		false,
		NULL, NULL, NULL
	},

	{
		{"update_process_title", PGC_SUSET, PROCESS_TITLE,
			gettext_noop("Updates the process title to show the active SQL command."),
			gettext_noop("Enables updating of the process title every time a new SQL command is received by the server.")
		},
		&update_process_title,
#ifdef WIN32
		false,
#else
		true,
#endif
		NULL, NULL, NULL
	},

	{
		{"autovacuum", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Starts the autovacuum subprocess."),
			NULL
		},
		&autovacuum_start_daemon,
		true,
		NULL, NULL, NULL
	},

	{
		{"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
			gettext_noop("Generates debugging output for LISTEN and NOTIFY."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&Trace_notify,
		false,
		NULL, NULL, NULL
	},

#ifdef LOCK_DEBUG
	{
		{"trace_locks", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Emits information about lock usage."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&Trace_locks,
		false,
		NULL, NULL, NULL
	},
	{
		{"trace_userlocks", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Emits information about user lock usage."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&Trace_userlocks,
		false,
		NULL, NULL, NULL
	},
	{
		{"trace_lwlocks", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Emits information about lightweight lock usage."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&Trace_lwlocks,
		false,
		NULL, NULL, NULL
	},
	{
		{"debug_deadlocks", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Dumps information about all current locks when a deadlock timeout occurs."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&Debug_deadlocks,
		false,
		NULL, NULL, NULL
	},
#endif

	{
		{"log_lock_waits", PGC_SUSET, LOGGING_WHAT,
			gettext_noop("Logs long lock waits."),
			NULL
		},
		&log_lock_waits,
		false,
		NULL, NULL, NULL
	},
	{
		{"log_recovery_conflict_waits", PGC_SIGHUP, LOGGING_WHAT,
			gettext_noop("Logs standby recovery conflict waits."),
			NULL
		},
		&log_recovery_conflict_waits,
		false,
		NULL, NULL, NULL
	},
	{
		{"log_hostname", PGC_SIGHUP, LOGGING_WHAT,
			gettext_noop("Logs the host name in the connection logs."),
			gettext_noop("By default, connection logs only show the IP address "
						 "of the connecting host. If you want them to show the host name you "
						 "can turn this on, but depending on your host name resolution "
						 "setup it might impose a non-negligible performance penalty.")
		},
		&log_hostname,
		false,
		NULL, NULL, NULL
	},
	{
		{"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
			gettext_noop("Treats \"expr=NULL\" as \"expr IS NULL\"."),
			gettext_noop("When turned on, expressions of the form expr = NULL "
						 "(or NULL = expr) are treated as expr IS NULL, that is, they "
						 "return true if expr evaluates to the null value, and false "
						 "otherwise. The correct behavior of expr = NULL is to always "
						 "return null (unknown).")
		},
		&Transform_null_equals,
		false,
		NULL, NULL, NULL
	},
	{
		{"db_user_namespace", PGC_SIGHUP, CONN_AUTH_AUTH,
			gettext_noop("Enables per-database user names."),
			NULL
		},
		&Db_user_namespace,
		false,
		NULL, NULL, NULL
	},
	{
		{"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the default read-only status of new transactions."),
			NULL,
			GUC_REPORT
		},
		&DefaultXactReadOnly,
		false,
		NULL, NULL, NULL
	},
	{
		{"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the current transaction's read-only status."),
			NULL,
			GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&XactReadOnly,
		false,
		check_transaction_read_only, NULL, NULL
	},
	{
		{"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the default deferrable status of new transactions."),
			NULL
		},
		&DefaultXactDeferrable,
		false,
		NULL, NULL, NULL
	},
	{
		{"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
			NULL,
			GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&XactDeferrable,
		false,
		check_transaction_deferrable, NULL, NULL
	},
	{
		{"row_security", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Enable row security."),
			gettext_noop("When enabled, row security will be applied to all users.")
		},
		&row_security,
		true,
		NULL, NULL, NULL
	},
	{
		{"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Check routine bodies during CREATE FUNCTION and CREATE PROCEDURE."),
			NULL
		},
		&check_function_bodies,
		true,
		NULL, NULL, NULL
	},
	{
		{"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("Enable input of NULL elements in arrays."),
			gettext_noop("When turned on, unquoted NULL in an array input "
						 "value means a null value; "
						 "otherwise it is taken literally.")
		},
		&Array_nulls,
		true,
		NULL, NULL, NULL
	},

	/*
 * 决定提议的自定义变量名称是否被允许。
 *
 * 它必须是两个或更多用点分隔的标识符，其中标识符的规则与scan.l一致。  （如果您更改此规则，请调整find_option()中的errdetail。）
 */
	{
		{"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("WITH OIDS is no longer supported; this can only be false."),
			NULL,
			GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
		},
		&default_with_oids,
		false,
		fc_check_default_with_oids, NULL, NULL
	},
	{
		{"logging_collector", PGC_POSTMASTER, LOGGING_WHERE,
			gettext_noop("Start a subprocess to capture stderr, csvlog and/or jsonlog into log files."),
			NULL
		},
		&Logging_collector,
		false,
		NULL, NULL, NULL
	},
	{
		{"log_truncate_on_rotation", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Truncate existing log files of same name during log rotation."),
			NULL
		},
		&Log_truncate_on_rotation,
		false,
		NULL, NULL, NULL
	},

#ifdef TRACE_SORT
	{
		{"trace_sort", PGC_USERSET, DEVELOPER_OPTIONS,
			gettext_noop("Emit information about resource usage in sorting."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&trace_sort,
		false,
		NULL, NULL, NULL
	},
#endif

#ifdef TRACE_SYNCSCAN
	/* 空名称组件 */
	{
		{"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
			gettext_noop("Generate debugging output for synchronized scanning."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&trace_syncscan,
		false,
		NULL, NULL, NULL
	},
#endif

#ifdef DEBUG_BOUNDED_SORT
	/* 空名称组件 */
	{
		{
			"optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
			gettext_noop("Enable bounded sorting using heap sort."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
		},
		&optimize_bounded_sort,
		true,
		NULL, NULL, NULL
	},
#endif

#ifdef WAL_DEBUG
	{
		{"wal_debug", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Emit WAL-related debugging output."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&XLOG_DEBUG,
		false,
		NULL, NULL, NULL
	},
#endif

	{
		{"integer_datetimes", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows whether datetimes are integer based."),
			NULL,
			GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&integer_datetimes,
		true,
		NULL, NULL, NULL
	},

	{
		{"krb_caseins_users", PGC_SIGHUP, CONN_AUTH_AUTH,
			gettext_noop("Sets whether Kerberos and GSSAPI user names should be treated as case-insensitive."),
			NULL
		},
		&pg_krb_caseins_users,
		false,
		NULL, NULL, NULL
	},

	{
		{"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("Warn about backslash escapes in ordinary string literals."),
			NULL
		},
		&escape_string_warning,
		true,
		NULL, NULL, NULL
	},

	{
		{"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("Causes '...' strings to treat backslashes literally."),
			NULL,
			GUC_REPORT
		},
		&standard_conforming_strings,
		true,
		NULL, NULL, NULL
	},

	{
		{"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("Enable synchronized sequential scans."),
			NULL
		},
		&synchronize_seqscans,
		true,
		NULL, NULL, NULL
	},

	{
		{"recovery_target_inclusive", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Sets whether to include or exclude transaction with recovery target."),
			NULL
		},
		&recoveryTargetInclusive,
		true,
		NULL, NULL, NULL
	},

	{
		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
			gettext_noop("Allows connections and queries during recovery."),
			NULL
		},
		&EnableHotStandby,
		true,
		NULL, NULL, NULL
	},

	{
		{"hot_standby_feedback", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Allows feedback from a hot standby to the primary that will avoid query conflicts."),
			NULL
		},
		&hot_standby_feedback,
		false,
		NULL, NULL, NULL
	},

	{
		{"in_hot_standby", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows whether hot standby is currently active."),
			NULL,
			GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&in_hot_standby,
		false,
		NULL, NULL, fc_show_in_hot_standby
	},

	{
		{"allow_system_table_mods", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Allows modifications of the structure of system tables."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&allowSystemTableMods,
		false,
		NULL, NULL, NULL
	},

	{
		{"ignore_system_indexes", PGC_BACKEND, DEVELOPER_OPTIONS,
			gettext_noop("Disables reading from system indexes."),
			gettext_noop("It does not prevent updating the indexes, so it is safe "
						 "to use.  The worst consequence is slowness."),
			GUC_NOT_IN_SAMPLE
		},
		&IgnoreSystemIndexes,
		false,
		NULL, NULL, NULL
	},

	{
		{"allow_in_place_tablespaces", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Allows tablespaces directly inside pg_tblspc, for testing."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&allow_in_place_tablespaces,
		false,
		NULL, NULL, NULL
	},

	{
		{"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("Enables backward compatibility mode for privilege checks on large objects."),
			gettext_noop("Skips privilege checks when reading or modifying large objects, "
						 "for compatibility with PostgreSQL releases prior to 9.0.")
		},
		&lo_compat_privileges,
		false,
		NULL, NULL, NULL
	},

	{
		{"quote_all_identifiers", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("When generating SQL fragments, quote all identifiers."),
			NULL,
		},
		&quote_all_identifiers,
		false,
		NULL, NULL, NULL
	},

	{
		{"data_checksums", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows whether data checksums are turned on for this cluster."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_RUNTIME_COMPUTED
		},
		&data_checksums,
		false,
		NULL, NULL, NULL
	},

	{
		{"syslog_sequence_numbers", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Add sequence number to syslog messages to avoid duplicate suppression."),
			NULL
		},
		&syslog_sequence_numbers,
		true,
		NULL, NULL, NULL
	},

	{
		{"syslog_split_messages", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Split messages sent to syslog by lines and to fit into 1024 bytes."),
			NULL
		},
		&syslog_split_messages,
		true,
		NULL, NULL, NULL
	},

	{
		{"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
			gettext_noop("Controls whether Gather and Gather Merge also run subplans."),
			gettext_noop("Should gather nodes also run subplans or just gather tuples?"),
			GUC_EXPLAIN
		},
		&parallel_leader_participation,
		true,
		NULL, NULL, NULL
	},

	{
		{"jit", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Allow JIT compilation."),
			NULL,
			GUC_EXPLAIN
		},
		&jit_enabled,
		true,
		NULL, NULL, NULL
	},

	{
		{"jit_debugging_support", PGC_SU_BACKEND, DEVELOPER_OPTIONS,
			gettext_noop("Register JIT-compiled functions with debugger."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&jit_debugging_support,
		false,

		/* 作为非第一个字符是可以的 */
		NULL, NULL, NULL
	},

	{
		{"jit_dump_bitcode", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Write out LLVM bitcode to facilitate JIT debugging."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&jit_dump_bitcode,
		false,
		NULL, NULL, NULL
	},

	{
		{"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS,
			gettext_noop("Allow JIT compilation of expressions."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&jit_expressions,
		true,
		NULL, NULL, NULL
	},

	{
		{"jit_profiling_support", PGC_SU_BACKEND, DEVELOPER_OPTIONS,
			gettext_noop("Register JIT-compiled functions with perf profiler."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&jit_profiling_support,
		false,

		/* 作为非第一个字符是可以的 */
		NULL, NULL, NULL
	},

	{
		{"jit_tuple_deforming", PGC_USERSET, DEVELOPER_OPTIONS,
			gettext_noop("Allow JIT compilation of tuple deforming."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&jit_tuple_deforming,
		true,
		NULL, NULL, NULL
	},

	{
		{"data_sync_retry", PGC_POSTMASTER, ERROR_HANDLING_OPTIONS,
			gettext_noop("Whether to continue running after a failure to sync data files."),
		},
		&data_sync_retry,
		false,
		NULL, NULL, NULL
	},

	{
		{"wal_receiver_create_temp_slot", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets whether a WAL receiver should create a temporary replication slot if no permanent slot is configured."),
		},
		&wal_receiver_create_temp_slot,
		false,
		NULL, NULL, NULL
	},
#ifdef FDDGM
	{
		{"allow_change_tde_encrpttype", PGC_SIGHUP, 0,
			gettext_noop("allow change TDE encrypt type."),
			NULL,
			GUC_NO_SHOW_ALL
		},
		&g_fdballowChangeTdeEncryptType,
		false,
		NULL, NULL, NULL
	},
#endif //FDDGM

	/* 如果我们至少找到一个分隔符，则可以 */
	{
		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
	}
};


static struct config_int ConfigureNamesInt[] =
{
	{
		{"archive_timeout", PGC_SIGHUP, WAL_ARCHIVING,
			gettext_noop("Sets the amount of time to wait before forcing a "
						 "switch to the next WAL file."),
			NULL,
			GUC_UNIT_S
		},
		&XLogArchiveTimeout,
		0, 0, INT_MAX / 2,
		NULL, NULL, NULL
	},
	{
		{"post_auth_delay", PGC_BACKEND, DEVELOPER_OPTIONS,
			gettext_noop("Sets the amount of time to wait after "
						 "authentication on connection startup."),
			gettext_noop("This allows attaching a debugger to the process."),
			GUC_NOT_IN_SAMPLE | GUC_UNIT_S
		},
		&PostAuthDelay,
		0, 0, INT_MAX / 1000000,
		NULL, NULL, NULL
	},
	{
		{"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Sets the default statistics target."),
			gettext_noop("This applies to table columns that have not had a "
						 "column-specific target set via ALTER TABLE SET STATISTICS.")
		},
		&default_statistics_target,
		100, 1, 10000,
		NULL, NULL, NULL
	},
	{
		{"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Sets the FROM-list size beyond which subqueries "
						 "are not collapsed."),
			gettext_noop("The planner will merge subqueries into upper "
						 "queries if the resulting FROM list would have no more than "
						 "this many items."),
			GUC_EXPLAIN
		},
		&from_collapse_limit,
		8, 1, INT_MAX,
		NULL, NULL, NULL
	},
	{
		{"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Sets the FROM-list size beyond which JOIN "
						 "constructs are not flattened."),
			gettext_noop("The planner will flatten explicit JOIN "
						 "constructs into lists of FROM items whenever a "
						 "list of no more than this many items would result."),
			GUC_EXPLAIN
		},
		&join_collapse_limit,
		8, 1, INT_MAX,
		NULL, NULL, NULL
	},
	{
		{"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
			gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
			NULL,
			GUC_EXPLAIN
		},
		&geqo_threshold,
		12, 2, INT_MAX,
		NULL, NULL, NULL
	},
	{
		{"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
			gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."),
			NULL,
			GUC_EXPLAIN
		},
		&Geqo_effort,
		DEFAULT_GEQO_EFFORT, MIN_GEQO_EFFORT, MAX_GEQO_EFFORT,
		NULL, NULL, NULL
	},
	{
		{"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
			gettext_noop("GEQO: number of individuals in the population."),
			gettext_noop("Zero selects a suitable default value."),
			GUC_EXPLAIN
		},
		&Geqo_pool_size,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},
	{
		{"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
			gettext_noop("GEQO: number of iterations of the algorithm."),
			gettext_noop("Zero selects a suitable default value."),
			GUC_EXPLAIN
		},
		&Geqo_generations,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		/*
 * 创建并添加一个占位符变量以用于自定义变量名称。
 */
		{"deadlock_timeout", PGC_SUSET, LOCK_MANAGEMENT,
			gettext_noop("Sets the time to wait on a lock before checking for deadlock."),
			NULL,
			GUC_UNIT_MS
		},
		&DeadlockTimeout,
		1000, 1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"max_standby_archive_delay", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the maximum delay before canceling queries when a hot standby server is processing archived WAL data."),
			NULL,
			GUC_UNIT_MS
		},
		&max_standby_archive_delay,
		30 * 1000, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"max_standby_streaming_delay", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the maximum delay before canceling queries when a hot standby server is processing streamed WAL data."),
			NULL,
			GUC_UNIT_MS
		},
		&max_standby_streaming_delay,
		30 * 1000, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"recovery_min_apply_delay", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the minimum delay for applying changes during recovery."),
			NULL,
			GUC_UNIT_MS
		},
		&recovery_min_apply_delay,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the maximum interval between WAL receiver status reports to the sending server."),
			NULL,
			GUC_UNIT_S
		},
		&wal_receiver_status_interval,
		10, 0, INT_MAX / 1000,
		NULL, NULL, NULL
	},

	{
		{"wal_receiver_timeout", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the maximum wait time to receive data from the sending server."),
			NULL,
			GUC_UNIT_MS
		},
		&wal_receiver_timeout,
		60 * 1000, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"max_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the maximum number of concurrent connections."),
			NULL
		},
		&MaxConnections,
		100, 1, MAX_BACKENDS,
		fc_check_maxconnections, NULL, NULL
	},

	{
		/*
	 * char*在结构的末尾分配，因为我们没有
	 * 'static'的位置指向。请注意，当前值，以及
	 * 引导和重置值，最初为NULL。
	 */
		{"superuser_reserved_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the number of connection slots reserved for superusers."),
			NULL
		},
		&ReservedBackends,
		3, 0, MAX_BACKENDS,
		NULL, NULL, NULL
	},

	{
		{"min_dynamic_shared_memory", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("Amount of dynamic shared memory reserved at startup."),
			NULL,
			GUC_UNIT_MB
		},
		&min_dynamic_shared_memory,
		0, 0, (int) Min((size_t) INT_MAX, SIZE_MAX / (1024 * 1024)),
		NULL, NULL, NULL
	},

	/*
 * 查找选项“名称”。如果存在，则返回指向其记录的指针。
 * 否则，如果create_placeholders为真且名称是有效的
 * 自定义变量名称，我们将创建并返回一个占位符记录。
 * 否则，如果skip_errors为真，我们将静默返回NULL以处理
 * 不被识别或无效的名称。否则，错误将在
 * 错误级别elevel（如果小于ERROR则返回NULL）下报告。
 *
 * 注意：内部错误，主要是内存不足，无论skip_errors如何，
 * 都会引发elevel级别的报告和NULL返回。因此，调用者必须
 * 在elevel < ERROR时处理NULL返回，但他们不需要
 * 发出任何额外的错误消息。（实际上，内部错误
 * 只能在create_placeholders为真时发生，因此传递
 * false的调用者不需要对此进行过多考虑。）
 */
	{
		{"shared_buffers", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("Sets the number of shared memory buffers used by the server."),
			NULL,
			GUC_UNIT_BLOCKS
		},
		&NBuffers,
		16384, 16, INT_MAX / 2,
		NULL, NULL, NULL
	},

	{
		{"shared_memory_size", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the size of the server's main shared memory area (rounded up to the nearest MB)."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_UNIT_MB | GUC_RUNTIME_COMPUTED
		},
		&shared_memory_size_mb,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"shared_memory_size_in_huge_pages", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the number of huge pages needed for the main shared memory area."),
			gettext_noop("-1 indicates that the value could not be determined."),
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_RUNTIME_COMPUTED
		},
		&shared_memory_size_in_huge_pages,
		-1, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"temp_buffers", PGC_USERSET, RESOURCES_MEM,
			gettext_noop("Sets the maximum number of temporary buffers used by each session."),
			NULL,
			GUC_UNIT_BLOCKS | GUC_EXPLAIN
		},
		&num_temp_buffers,
		1024, 100, INT_MAX / 2,
		fc_check_temp_buffers, NULL, NULL
	},

	{
		{"port", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the TCP port the server listens on."),
			NULL
		},
		&PostPortNumber,
		DEF_PGPORT, 1, 65535,
		NULL, NULL, NULL
	},

	{
		{"unix_socket_permissions", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the access permissions of the Unix-domain socket."),
			gettext_noop("Unix-domain sockets use the usual Unix file system "
						 "permission set. The parameter value is expected "
						 "to be a numeric mode specification in the form "
						 "accepted by the chmod and umask system calls. "
						 "(To use the customary octal format the number must "
						 "start with a 0 (zero).)")
		},
		&Unix_socket_permissions,
		0777, 0000, 0777,
		NULL, NULL, fc_show_unix_socket_permissions
	},

	{
		{"log_file_mode", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the file permissions for log files."),
			gettext_noop("The parameter value is expected "
						 "to be a numeric mode specification in the form "
						 "accepted by the chmod and umask system calls. "
						 "(To use the customary octal format the number must "
						 "start with a 0 (zero).)")
		},
		&Log_file_mode,
		0600, 0000, 0777,
		NULL, NULL, fc_show_log_file_mode
	},


	{
		{"data_directory_mode", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the mode of the data directory."),
			gettext_noop("The parameter value is a numeric mode specification "
						 "in the form accepted by the chmod and umask system "
						 "calls. (To use the customary octal format the number "
						 "must start with a 0 (zero).)"),
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_RUNTIME_COMPUTED
		},
		&data_directory_mode,
		0700, 0000, 0777,
		NULL, NULL, fc_show_data_directory_mode
	},

	{
		{"work_mem", PGC_USERSET, RESOURCES_MEM,
			gettext_noop("Sets the maximum memory to be used for query workspaces."),
			gettext_noop("This much memory can be used by each internal "
						 "sort operation and hash table before switching to "
						 "temporary disk files."),
			GUC_UNIT_KB | GUC_EXPLAIN
		},
		&work_mem,
		4096, 64, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	{
		{"maintenance_work_mem", PGC_USERSET, RESOURCES_MEM,
			gettext_noop("Sets the maximum memory to be used for maintenance operations."),
			gettext_noop("This includes operations such as VACUUM and CREATE INDEX."),
			GUC_UNIT_KB
		},
		&maintenance_work_mem,
		65536, 1024, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	{
		{"logical_decoding_work_mem", PGC_USERSET, RESOURCES_MEM,
			gettext_noop("Sets the maximum memory to be used for logical decoding."),
			gettext_noop("This much memory can be used by each internal "
						 "reorder buffer before spilling to disk."),
			GUC_UNIT_KB
		},
		&logical_decoding_work_mem,
		65536, 64, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	/*
	 * 通过将const char **与struct config_generic *相等，
	 * 我们假设name字段在config_generic中是第一个。
	 */
	{
		{"max_stack_depth", PGC_SUSET, RESOURCES_MEM,
			gettext_noop("Sets the maximum stack depth, in kilobytes."),
			NULL,
			GUC_UNIT_KB
		},
		&max_stack_depth,
		100, 100, MAX_KILOBYTES,
		check_max_stack_depth, assign_max_stack_depth, NULL
	},

	{
		{"temp_file_limit", PGC_SUSET, RESOURCES_DISK,
			gettext_noop("Limits the total size of all temporary files used by each process."),
			gettext_noop("-1 means no limit."),
			GUC_UNIT_KB
		},
		&temp_file_limit,
		-1, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
			gettext_noop("Vacuum cost for a page found in the buffer cache."),
			NULL
		},
		&VacuumCostPageHit,
		1, 0, 10000,
		NULL, NULL, NULL
	},

	{
		{"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
			gettext_noop("Vacuum cost for a page not found in the buffer cache."),
			NULL
		},
		&VacuumCostPageMiss,
		2, 0, 10000,
		NULL, NULL, NULL
	},

	{
		{"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
			gettext_noop("Vacuum cost for a page dirtied by vacuum."),
			NULL
		},
		&VacuumCostPageDirty,
		20, 0, 10000,
		NULL, NULL, NULL
	},

	{
		{"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
			gettext_noop("Vacuum cost amount available before napping."),
			NULL
		},
		&VacuumCostLimit,
		200, 1, 10000,
		NULL, NULL, NULL
	},

	{
		{"autovacuum_vacuum_cost_limit", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Vacuum cost amount available before napping, for autovacuum."),
			NULL
		},
		&autovacuum_vac_cost_limit,
		-1, -1, 10000,
		NULL, NULL, NULL
	},

	{
		{"max_files_per_process", PGC_POSTMASTER, RESOURCES_KERNEL,
			gettext_noop("Sets the maximum number of simultaneously open files for each server process."),
			NULL
		},
		&max_files_per_process,
		1000, 64, INT_MAX,
		NULL, NULL, NULL
	},

	/*
	 * 检查名称是否是变量的过时名称。我们假定
	 * 支持的旧名称集合足够短，以至于暴力搜索是
	 * 最好的方法。
	 */
	{
		{"max_prepared_transactions", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("Sets the maximum number of simultaneously prepared transactions."),
			NULL
		},
		&max_prepared_xacts,
		0, 0, MAX_BACKENDS,
		NULL, NULL, NULL
	},

#ifdef LOCK_DEBUG
	{
		{"trace_lock_oidmin", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Sets the minimum OID of tables for tracking locks."),
			gettext_noop("Is used to avoid output on system tables."),
			GUC_NOT_IN_SAMPLE
		},
		&Trace_lock_oidmin,
		FirstNormalObjectId, 0, INT_MAX,
		NULL, NULL, NULL
	},
	{
		{"trace_lock_table", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Sets the OID of the table with unconditionally lock tracing."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&Trace_lock_table,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},
#endif

	{
		{"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the maximum allowed duration of any statement."),
			gettext_noop("A value of 0 turns off the timeout."),
			GUC_UNIT_MS
		},
		&StatementTimeout,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
			gettext_noop("A value of 0 turns off the timeout."),
			GUC_UNIT_MS
		},
		&LockTimeout,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."),
			gettext_noop("A value of 0 turns off the timeout."),
			GUC_UNIT_MS
		},
		&IdleInTransactionSessionTimeout,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the maximum allowed idle time between queries, when not in a transaction."),
			gettext_noop("A value of 0 turns off the timeout."),
			GUC_UNIT_MS
		},
		&IdleSessionTimeout,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Minimum age at which VACUUM should freeze a table row."),
			NULL
		},
		&vacuum_freeze_min_age,
		50000000, 0, 1000000000,
		NULL, NULL, NULL
	},

	{
		{"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
			NULL
		},
		&vacuum_freeze_table_age,
		150000000, 0, 2000000000,
		NULL, NULL, NULL
	},

	{
		{"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
			NULL
		},
		&vacuum_multixact_freeze_min_age,
		5000000, 0, 1000000000,
		NULL, NULL, NULL
	},

	{
		{"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
			NULL
		},
		&vacuum_multixact_freeze_table_age,
		150000000, 0, 2000000000,
		NULL, NULL, NULL
	},

	{
		{"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_PRIMARY,
			gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."),
			NULL
		},
		&vacuum_defer_cleanup_age,
		0, 0, 1000000,			/*
		 * 检查名称是否有效，如果有效，则添加一个占位符。如果它
		 * 不包含分隔符，则不要假定它是一个
		 * 占位符。
		 */
		NULL, NULL, NULL
	},
	{
		{"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
			NULL
		},
		&vacuum_failsafe_age,
		1600000000, 0, 2100000000,
		NULL, NULL, NULL
	},
	{
		{"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
			NULL
		},
		&vacuum_multixact_failsafe_age,
		1600000000, 0, 2100000000,
		NULL, NULL, NULL
	},

	/*
	 * 检查名称是否是变量的过时名称。我们假定
	 * 支持的旧名称集合足够短，以至于暴力搜索是
	 * 最好的方法。
	 */
	{
		{"max_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT,
			gettext_noop("Sets the maximum number of locks per transaction."),
			gettext_noop("The shared lock table is sized on the assumption that "
						 "at most max_locks_per_transaction * max_connections distinct "
						 "objects will need to be locked at any one time.")
		},
		&max_locks_per_xact,
		64, 10, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"max_pred_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT,
			gettext_noop("Sets the maximum number of predicate locks per transaction."),
			gettext_noop("The shared predicate lock table is sized on the assumption that "
						 "at most max_pred_locks_per_transaction * max_connections distinct "
						 "objects will need to be locked at any one time.")
		},
		&max_predicate_locks_per_xact,
		64, 10, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"max_pred_locks_per_relation", PGC_SIGHUP, LOCK_MANAGEMENT,
			gettext_noop("Sets the maximum number of predicate-locked pages and tuples per relation."),
			gettext_noop("If more than this total of pages and tuples in the same relation are locked "
						 "by a connection, those locks are replaced by a relation-level lock.")
		},
		&max_predicate_locks_per_relation,
		-2, INT_MIN, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"max_pred_locks_per_page", PGC_SIGHUP, LOCK_MANAGEMENT,
			gettext_noop("Sets the maximum number of predicate-locked tuples per page."),
			gettext_noop("If more than this number of tuples on the same page are locked "
						 "by a connection, those locks are replaced by a page-level lock.")
		},
		&max_predicate_locks_per_page,
		2, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"authentication_timeout", PGC_SIGHUP, CONN_AUTH_AUTH,
			gettext_noop("Sets the maximum allowed time to complete client authentication."),
			NULL,
			GUC_UNIT_S
		},
		&AuthenticationTimeout,
		60, 1, 600,
		NULL, NULL, NULL
	},

	{
		/* ...并且它不能与任何先前保留的前缀匹配 */
		{"pre_auth_delay", PGC_SIGHUP, DEVELOPER_OPTIONS,
			gettext_noop("Sets the amount of time to wait before "
						 "authentication on connection startup."),
			gettext_noop("This allows attaching a debugger to the process."),
			GUC_NOT_IN_SAMPLE | GUC_UNIT_S
		},
		&PreAuthDelay,
		0, 0, 60,
		NULL, NULL, NULL
	},

	{
		{"wal_decode_buffer_size", PGC_POSTMASTER, WAL_RECOVERY,
			gettext_noop("Buffer size for reading ahead in the WAL during recovery."),
			gettext_noop("Maximum distance to read ahead in the WAL to prefetch referenced data blocks."),
			GUC_UNIT_BYTE
		},
		&wal_decode_buffer_size,
		512 * 1024, 64 * 1024, MaxAllocSize,
		NULL, NULL, NULL
	},

	{
		{"wal_keep_size", PGC_SIGHUP, REPLICATION_SENDING,
			gettext_noop("Sets the size of WAL files held for standby servers."),
			NULL,
			GUC_UNIT_MB
		},
		&wal_keep_size_mb,
		0, 0, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	{
		{"min_wal_size", PGC_SIGHUP, WAL_CHECKPOINTS,
			gettext_noop("Sets the minimum size to shrink the WAL to."),
			NULL,
			GUC_UNIT_MB
		},
		&min_wal_size_mb,
		DEFAULT_MIN_WAL_SEGS * (DEFAULT_XLOG_SEG_SIZE / (1024 * 1024)),
		2, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	{
		{"max_wal_size", PGC_SIGHUP, WAL_CHECKPOINTS,
			gettext_noop("Sets the WAL size that triggers a checkpoint."),
			NULL,
			GUC_UNIT_MB
		},
		&max_wal_size_mb,
		DEFAULT_MAX_WAL_SEGS * (DEFAULT_XLOG_SEG_SIZE / (1024 * 1024)),
		2, MAX_KILOBYTES,
		NULL, assign_max_wal_size, NULL
	},

	{
		{"checkpoint_timeout", PGC_SIGHUP, WAL_CHECKPOINTS,
			gettext_noop("Sets the maximum time between automatic WAL checkpoints."),
			NULL,
			GUC_UNIT_S
		},
		&CheckPointTimeout,
		300, 30, 86400,
		NULL, NULL, NULL
	},

	{
		{"checkpoint_warning", PGC_SIGHUP, WAL_CHECKPOINTS,
			gettext_noop("Sets the maximum time before warning if checkpoints "
						 "triggered by WAL volume happen too frequently."),
			gettext_noop("Write a message to the server log if checkpoints "
						 "caused by the filling of WAL segment files happen more "
						 "frequently than this amount of time. "
						 "Zero turns off the warning."),
			GUC_UNIT_S
		},
		&CheckPointWarning,
		30, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"checkpoint_flush_after", PGC_SIGHUP, WAL_CHECKPOINTS,
			gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
			NULL,
			GUC_UNIT_BLOCKS
		},
		&checkpoint_flush_after,
		DEFAULT_CHECKPOINT_FLUSH_AFTER, 0, WRITEBACK_MAX_PENDING_FLUSHES,
		NULL, NULL, NULL
	},

	{
		{"wal_buffers", PGC_POSTMASTER, WAL_SETTINGS,
			gettext_noop("Sets the number of disk-page buffers in shared memory for WAL."),
			NULL,
			GUC_UNIT_XBLOCKS
		},
		&XLOGbuffers,
		-1, -1, (INT_MAX / XLOG_BLCKSZ),
		check_wal_buffers, NULL, NULL
	},

	{
		{"wal_writer_delay", PGC_SIGHUP, WAL_SETTINGS,
			gettext_noop("Time between WAL flushes performed in the WAL writer."),
			NULL,
			GUC_UNIT_MS
		},
		&WalWriterDelay,
		200, 1, 10000,
		NULL, NULL, NULL
	},

	{
		{"wal_writer_flush_after", PGC_SIGHUP, WAL_SETTINGS,
			gettext_noop("Amount of WAL written out by WAL writer that triggers a flush."),
			NULL,
			GUC_UNIT_XBLOCKS
		},
		&WalWriterFlushAfter,
		(1024 * 1024) / XLOG_BLCKSZ, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"wal_skip_threshold", PGC_USERSET, WAL_SETTINGS,
			gettext_noop("Minimum size of new file to fsync instead of writing WAL."),
			NULL,
			GUC_UNIT_KB
		},
		&wal_skip_threshold,
		2048, 0, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	{
		{"max_wal_senders", PGC_POSTMASTER, REPLICATION_SENDING,
			gettext_noop("Sets the maximum number of simultaneously running WAL sender processes."),
			NULL
		},
		&max_wal_senders,
		10, 0, MAX_BACKENDS,
		fc_check_max_wal_senders, NULL, NULL
	},

	{
		/* 可以，创建它 */
		{"max_replication_slots", PGC_POSTMASTER, REPLICATION_SENDING,
			gettext_noop("Sets the maximum number of simultaneously defined replication slots."),
			NULL
		},
		&max_replication_slots,
		10, 0, MAX_BACKENDS /* 未知名称 */ ,
		NULL, NULL, NULL
	},

	{
		{"max_slot_wal_keep_size", PGC_SIGHUP, REPLICATION_SENDING,
			gettext_noop("Sets the maximum WAL size that can be reserved by replication slots."),
			gettext_noop("Replication slots will be marked as failed, and segments released "
						 "for deletion or recycling, if this much space is occupied by WAL "
						 "on disk."),
			GUC_UNIT_MB
		},
		&max_slot_wal_keep_size_mb,
		-1, -1, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	{
		{"wal_sender_timeout", PGC_USERSET, REPLICATION_SENDING,
			gettext_noop("Sets the maximum time to wait for WAL replication."),
			NULL,
			GUC_UNIT_MS
		},
		&wal_sender_timeout,
		60 * 1000, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"commit_delay", PGC_SUSET, WAL_SETTINGS,
			gettext_noop("Sets the delay in microseconds between transaction commit and "
						 "flushing WAL to disk."),
			NULL
			/*
 * 用于qsorting和bsearching guc_variables数组的比较器
 */
		},
		&CommitDelay,
		0, 0, 100000,
		NULL, NULL, NULL
	},

	{
		{"commit_siblings", PGC_USERSET, WAL_SETTINGS,
			gettext_noop("Sets the minimum number of concurrent open transactions "
						 "required before performing commit_delay."),
			NULL
		},
		&CommitSiblings,
		5, 0, 1000,
		NULL, NULL, NULL
	},

	{
		{"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the number of digits displayed for floating-point values."),
			gettext_noop("This affects real, double precision, and geometric data types. "
						 "A zero or negative parameter value is added to the standard "
						 "number of digits (FLT_DIG or DBL_DIG as appropriate). "
						 "Any value greater than zero selects precise output mode.")
		},
		&extra_float_digits,
		1, -15, 3,
		NULL, NULL, NULL
	},

	{
		{"log_min_duration_sample", PGC_SUSET, LOGGING_WHEN,
			gettext_noop("Sets the minimum execution time above which "
						 "a sample of statements will be logged."
						 " Sampling is determined by log_statement_sample_rate."),
			gettext_noop("Zero logs a sample of all queries. -1 turns this feature off."),
			GUC_UNIT_MS
		},
		&log_min_duration_sample,
		-1, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"log_min_duration_statement", PGC_SUSET, LOGGING_WHEN,
			gettext_noop("Sets the minimum execution time above which "
						 "all statements will be logged."),
			gettext_noop("Zero prints all queries. -1 turns this feature off."),
			GUC_UNIT_MS
		},
		&log_min_duration_statement,
		-1, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"log_autovacuum_min_duration", PGC_SIGHUP, LOGGING_WHAT,
			gettext_noop("Sets the minimum execution time above which "
						 "autovacuum actions will be logged."),
			gettext_noop("Zero prints all actions. -1 turns autovacuum logging off."),
			GUC_UNIT_MS
		},
		&Log_autovacuum_min_duration,
		600000, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"log_parameter_max_length", PGC_SUSET, LOGGING_WHAT,
			gettext_noop("Sets the maximum length in bytes of data logged for bind "
						 "parameter values when logging statements."),
			gettext_noop("-1 to print values in full."),
			GUC_UNIT_BYTE
		},
		&log_parameter_max_length,
		-1, -1, INT_MAX / 2,
		NULL, NULL, NULL
	},

	{
		{"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
			gettext_noop("Sets the maximum length in bytes of data logged for bind "
						 "parameter values when logging statements, on error."),
			gettext_noop("-1 to print values in full."),
			GUC_UNIT_BYTE
		},
		&log_parameter_max_length_on_error,
		0, -1, INT_MAX / 2,
		NULL, NULL, NULL
	},

	{
		{"bgwriter_delay", PGC_SIGHUP, RESOURCES_BGWRITER,
			gettext_noop("Background writer sleep time between rounds."),
			NULL,
			GUC_UNIT_MS
		},
		&BgWriterDelay,
		200, 10, 10000,
		NULL, NULL, NULL
	},

	{
		{"bgwriter_lru_maxpages", PGC_SIGHUP, RESOURCES_BGWRITER,
			gettext_noop("Background writer maximum number of LRU pages to flush per round."),
			NULL
		},
		&bgwriter_lru_maxpages,
		100, 0, INT_MAX / 2,	/*
 * GUC名称的基本比较函数
 */
		NULL, NULL, NULL
	},

	{
		{"bgwriter_flush_after", PGC_SIGHUP, RESOURCES_BGWRITER,
			gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
			NULL,
			GUC_UNIT_BLOCKS
		},
		&bgwriter_flush_after,
		DEFAULT_BGWRITER_FLUSH_AFTER, 0, WRITEBACK_MAX_PENDING_FLUSHES,
		NULL, NULL, NULL
	},

	{
		{"effective_io_concurrency",
			PGC_USERSET,
			RESOURCES_ASYNCHRONOUS,
			gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."),
			NULL,
			GUC_EXPLAIN
		},
		&effective_io_concurrency,
#ifdef USE_PREFETCH
		1,
#else
		0,
#endif
		0, MAX_IO_CONCURRENCY,
		fc_check_effective_io_concurrency, NULL, NULL
	},

	{
		{"maintenance_io_concurrency",
			PGC_USERSET,
			RESOURCES_ASYNCHRONOUS,
			gettext_noop("A variant of effective_io_concurrency that is used for maintenance work."),
			NULL,
			GUC_EXPLAIN
		},
		&maintenance_io_concurrency,
#ifdef USE_PREFETCH
		10,
#else
		0,
#endif
		0, MAX_IO_CONCURRENCY,
		fc_check_maintenance_io_concurrency, fc_assign_maintenance_io_concurrency,
		NULL
	},

	{
		{"backend_flush_after", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
			gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
			NULL,
			GUC_UNIT_BLOCKS
		},
		&backend_flush_after,
		DEFAULT_BACKEND_FLUSH_AFTER, 0, WRITEBACK_MAX_PENDING_FLUSHES,
		NULL, NULL, NULL
	},

	{
		{"max_worker_processes",
			PGC_POSTMASTER,
			RESOURCES_ASYNCHRONOUS,
			gettext_noop("Maximum number of concurrent worker processes."),
			NULL,
		},
		&max_worker_processes,
		8, 0, MAX_BACKENDS,
		fc_check_max_worker_processes, NULL, NULL
	},

	{
		{"max_logical_replication_workers",
			PGC_POSTMASTER,
			REPLICATION_SUBSCRIBERS,
			gettext_noop("Maximum number of logical replication worker processes."),
			NULL,
		},
		&max_logical_replication_workers,
		4, 0, MAX_BACKENDS,
		NULL, NULL, NULL
	},

	{
		{"max_sync_workers_per_subscription",
			PGC_SIGHUP,
			REPLICATION_SUBSCRIBERS,
			gettext_noop("Maximum number of table synchronization workers per subscription."),
			NULL,
		},
		&max_sync_workers_per_subscription,
		2, 0, MAX_BACKENDS,
		NULL, NULL, NULL
	},

	{
		{"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the amount of time to wait before forcing "
						 "log file rotation."),
			NULL,
			GUC_UNIT_MIN
		},
		&Log_RotationAge,
		HOURS_PER_DAY * MINS_PER_HOUR, 0, INT_MAX / SECS_PER_MINUTE,
		NULL, NULL, NULL
	},

	{
		{"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the maximum size a log file can reach before "
						 "being rotated."),
			NULL,
			GUC_UNIT_KB
		},
		&Log_RotationSize,
		10 * 1024, 0, INT_MAX / 1024,
		NULL, NULL, NULL
	},

	{
		{"max_function_args", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the maximum number of function arguments."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&max_function_args,
		FUNC_MAX_ARGS, FUNC_MAX_ARGS, FUNC_MAX_ARGS,
		NULL, NULL, NULL
	},

	{
		{"max_index_keys", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the maximum number of index keys."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&max_index_keys,
		INDEX_MAX_KEYS, INDEX_MAX_KEYS, INDEX_MAX_KEYS,
		NULL, NULL, NULL
	},

	{
		{"max_identifier_length", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the maximum identifier length."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&max_identifier_length,
		NAMEDATALEN - 1, NAMEDATALEN - 1, NAMEDATALEN - 1,
		NULL, NULL, NULL
	},

	{
		{"block_size", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the size of a disk block."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&block_size,
		BLCKSZ, BLCKSZ, BLCKSZ,
		NULL, NULL, NULL
	},

	{
		{"segment_size", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the number of pages per disk file."),
			NULL,
			GUC_UNIT_BLOCKS | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&segment_size,
		RELSEG_SIZE, RELSEG_SIZE, RELSEG_SIZE,
		NULL, NULL, NULL
	},

	{
		{"wal_block_size", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the block size in the write ahead log."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&wal_block_size,
		XLOG_BLCKSZ, XLOG_BLCKSZ, XLOG_BLCKSZ,
		NULL, NULL, NULL
	},

	{
		{"wal_retrieve_retry_interval", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the time to wait before retrying to retrieve WAL "
						 "after a failed attempt."),
			NULL,
			GUC_UNIT_MS
		},
		&wal_retrieve_retry_interval,
		5000, 1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"wal_segment_size", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the size of write ahead log segments."),
			NULL,
			GUC_UNIT_BYTE | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_RUNTIME_COMPUTED
		},
		&wal_segment_size,
		DEFAULT_XLOG_SEG_SIZE,
		WalSegMinSize,
		WalSegMaxSize,
		NULL, NULL, NULL
	},

	{
		{"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Time to sleep between autovacuum runs."),
			NULL,
			GUC_UNIT_S
		},
		&autovacuum_naptime,
		60, 1, INT_MAX / 1000,
		NULL, NULL, NULL
	},
	{
		{"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Minimum number of tuple updates or deletes prior to vacuum."),
			NULL
		},
		&autovacuum_vac_thresh,
		50, 0, INT_MAX,
		NULL, NULL, NULL
	},
	{
		{"autovacuum_vacuum_insert_threshold", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums."),
			NULL
		},
		&autovacuum_vac_ins_thresh,
		1000, -1, INT_MAX,
		NULL, NULL, NULL
	},
	{
		{"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Minimum number of tuple inserts, updates, or deletes prior to analyze."),
			NULL
		},
		&autovacuum_anl_thresh,
		50, 0, INT_MAX,
		NULL, NULL, NULL
	},
	{
		/*
	 * 在这里必须抵制使用strcasecmp()的诱惑，因为
	 * 数组排序必须在setlocale()调用之间保持稳定。因此，构建
	 * 我们自己的具有简单ASCII-only小写的比较函数。
	 */
		{"autovacuum_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
			gettext_noop("Age at which to autovacuum a table to prevent transaction ID wraparound."),
			NULL
		},
		&autovacuum_freeze_max_age,

		/* a更长 */
		200000000, 100000, 2000000000,
		NULL, NULL, NULL
	},
	{
		/* b更长 */
		{"autovacuum_multixact_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
			gettext_noop("Multixact age at which to autovacuum a table to prevent multixact wraparound."),
			NULL
		},
		&autovacuum_multixact_freeze_max_age,
		400000000, 10000, 2000000000,
		NULL, NULL, NULL
	},
	{
		/*
	 * char*在结构的末尾分配，因为我们没有
	 * 'static'的位置指向。请注意，当前值，以及
	 * 引导和重置值，最初为NULL。
	 */
		{"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM,
			gettext_noop("Sets the maximum number of simultaneously running autovacuum worker processes."),
			NULL
		},
		&autovacuum_max_workers,
		3, 1, MAX_BACKENDS,
		fc_check_autovacuum_max_workers, NULL, NULL
	},

	{
		{"max_parallel_maintenance_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
			gettext_noop("Sets the maximum number of parallel processes per maintenance operation."),
			NULL
		},
		&max_parallel_maintenance_workers,
		2, 0, 1024,
		NULL, NULL, NULL
	},

	{
		{"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
			gettext_noop("Sets the maximum number of parallel processes per executor node."),
			NULL,
			GUC_EXPLAIN
		},
		&max_parallel_workers_per_gather,
		2, 0, MAX_PARALLEL_WORKER_LIMIT,
		NULL, NULL, NULL
	},

	{
		{"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
			gettext_noop("Sets the maximum number of parallel workers that can be active at one time."),
			NULL,
			GUC_EXPLAIN
		},
		&max_parallel_workers,
		8, 0, MAX_PARALLEL_WORKER_LIMIT,
		NULL, NULL, NULL
	},

	{
		{"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
			gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
			NULL,
			GUC_UNIT_KB
		},
		&autovacuum_work_mem,
		-1, -1, MAX_KILOBYTES,
		fc_check_autovacuum_work_mem, NULL, NULL
	},

	{
		{"old_snapshot_threshold", PGC_POSTMASTER, RESOURCES_ASYNCHRONOUS,
			gettext_noop("Time before a snapshot is too old to read pages changed after the snapshot was taken."),
			gettext_noop("A value of -1 disables this feature."),
			GUC_UNIT_MIN
		},
		&old_snapshot_threshold,
		-1, -1, MINS_PER_HOUR * HOURS_PER_DAY * 60,
		NULL, NULL, NULL
	},

	{
		{"tcp_keepalives_idle", PGC_USERSET, CONN_AUTH_SETTINGS,
			gettext_noop("Time between issuing TCP keepalives."),
			gettext_noop("A value of 0 uses the system default."),
			GUC_UNIT_S
		},
		&tcp_keepalives_idle,
		0, 0, INT_MAX,
		NULL, fc_assign_tcp_keepalives_idle, fc_show_tcp_keepalives_idle
	},

	{
		{"tcp_keepalives_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
			gettext_noop("Time between TCP keepalive retransmits."),
			gettext_noop("A value of 0 uses the system default."),
			GUC_UNIT_S
		},
		&tcp_keepalives_interval,
		0, 0, INT_MAX,
		NULL, fc_assign_tcp_keepalives_interval, fc_show_tcp_keepalives_interval
	},

	{
		{"ssl_renegotiation_limit", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("SSL renegotiation is no longer supported; this can only be 0."),
			NULL,
			GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
		},
		&ssl_renegotiation_limit,
		0, 0, 0,
		NULL, NULL, NULL
	},

	{
		{"tcp_keepalives_count", PGC_USERSET, CONN_AUTH_SETTINGS,
			gettext_noop("Maximum number of TCP keepalive retransmits."),
			gettext_noop("Number of consecutive keepalive retransmits that can be "
						 "lost before a connection is considered dead. A value of 0 uses the "
						 "system default."),
		},
		&tcp_keepalives_count,
		0, 0, INT_MAX,
		NULL, fc_assign_tcp_keepalives_count, fc_show_tcp_keepalives_count
	},

	{
		{"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
			gettext_noop("Sets the maximum allowed result for exact search by GIN."),
			NULL,
			0
		},
		&GinFuzzySearchLimit,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's assumption about the total size of the data caches."),
			gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. "
						 "This is measured in disk pages, which are normally 8 kB each."),
			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
		},
		&effective_cache_size,
		DEFAULT_EFFECTIVE_CACHE_SIZE, 1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the minimum amount of table data for a parallel scan."),
			gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),
			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
		},
		&min_parallel_table_scan_size,
		(8 * 1024 * 1024) / BLCKSZ, 0, INT_MAX / 3,
		NULL, NULL, NULL
	},

	{
		{"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the minimum amount of index data for a parallel scan."),
			gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."),
			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
		},
		&min_parallel_index_scan_size,
		(512 * 1024) / BLCKSZ, 0, INT_MAX / 3,
		NULL, NULL, NULL
	},

	{
		/* 应用旧GUC名称映射。 */
		{"server_version_num", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the server version as an integer."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&server_version_num,
		PG_VERSION_NUM, PG_VERSION_NUM, PG_VERSION_NUM,
		NULL, NULL, NULL
	},

	{
		{"log_temp_files", PGC_SUSET, LOGGING_WHAT,
			gettext_noop("Log the use of temporary files larger than this number of kilobytes."),
			gettext_noop("Zero logs all files. The default is -1 (turning this feature off)."),
			GUC_UNIT_KB
		},
		&log_temp_files,
		-1, -1, INT_MAX,
		NULL, NULL, NULL
	},

	{
		{"track_activity_query_size", PGC_POSTMASTER, STATS_CUMULATIVE,
			gettext_noop("Sets the size reserved for pg_stat_activity.query, in bytes."),
			NULL,
			GUC_UNIT_BYTE
		},
		&pgstat_track_activity_query_size,
		1024, 100, 1048576,
		NULL, NULL, NULL
	},

	{
		{"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the maximum size of the pending list for GIN index."),
			NULL,
			GUC_UNIT_KB
		},
		&gin_pending_list_limit,
		4096, 64, MAX_KILOBYTES,
		NULL, NULL, NULL
	},

	{
		{"tcp_user_timeout", PGC_USERSET, CONN_AUTH_SETTINGS,
			gettext_noop("TCP user timeout."),
			gettext_noop("A value of 0 uses the system default."),
			GUC_UNIT_MS
		},
		&tcp_user_timeout,
		0, 0, INT_MAX,
		NULL, fc_assign_tcp_user_timeout, fc_show_tcp_user_timeout
	},

	{
		{"huge_page_size", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("The size of huge page that should be requested."),
			NULL,
			GUC_UNIT_KB
		},
		&huge_page_size,
		0, 0, INT_MAX,
		fc_check_huge_page_size, NULL, NULL
	},

	{
		{"debug_discard_caches", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Aggressively flush system caches for debugging purposes."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&debug_discard_caches,
#ifdef DISCARD_CACHES_ENABLED
		/* 应用与guc_name_compare()匹配的大小写折叠。 */
#if defined(CLOBBER_CACHE_RECURSIVELY)
		3,
#elif defined(CLOBBER_CACHE_ALWAYS)
		1,
#else
		0,
#endif
		0, 5,
#else							/*
 * 检查是否应允许为给定名称创建pg_parameter_acl条目。
 * （这可以在规范化之前或之后应用。）
 */
		0, 0, 0,
#endif							/*
 * 检查是否应允许为给定名称创建pg_parameter_acl条目。
 * （这可以在规范化之前或之后应用。）
 */
		NULL, NULL, NULL
	},

	{
		{"client_connection_check_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the time interval between checks for disconnection while running queries."),
			NULL,
			GUC_UNIT_MS
		},
		&client_connection_check_interval,
		0, 0, INT_MAX,
		fc_check_client_connection_check_interval, NULL, NULL
	},

	{
		{"log_startup_progress_interval", PGC_SIGHUP, LOGGING_WHEN,
			gettext_noop("Time between progress updates for "
						 "long-running startup operations."),
			gettext_noop("0 turns this feature off."),
			GUC_UNIT_MS,
		},
		&log_startup_progress_interval,
		10000, 0, INT_MAX,
		NULL, NULL, NULL
	},

	/* 如果我们至少找到一个分隔符，则可以 */
	{
		{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
	}
};


static struct config_real ConfigureNamesReal[] =
{
	{
		{"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's estimate of the cost of a "
						 "sequentially fetched disk page."),
			NULL,
			GUC_EXPLAIN
		},
		&seq_page_cost,
		DEFAULT_SEQ_PAGE_COST, 0, DBL_MAX,
		NULL, NULL, NULL
	},
	{
		{"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's estimate of the cost of a "
						 "nonsequentially fetched disk page."),
			NULL,
			GUC_EXPLAIN
		},
		&random_page_cost,
		DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX,
		NULL, NULL, NULL
	},
	{
		{"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's estimate of the cost of "
						 "processing each tuple (row)."),
			NULL,
			GUC_EXPLAIN
		},
		&cpu_tuple_cost,
		DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX,
		NULL, NULL, NULL
	},
	{
		{"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's estimate of the cost of "
						 "processing each index entry during an index scan."),
			NULL,
			GUC_EXPLAIN
		},
		&cpu_index_tuple_cost,
		DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX,
		NULL, NULL, NULL
	},
	{
		{"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's estimate of the cost of "
						 "processing each operator or function call."),
			NULL,
			GUC_EXPLAIN
		},
		&cpu_operator_cost,
		DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX,
		NULL, NULL, NULL
	},
	{
		{"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's estimate of the cost of "
						 "passing each tuple (row) from worker to leader backend."),
			NULL,
			GUC_EXPLAIN
		},
		&parallel_tuple_cost,
		DEFAULT_PARALLEL_TUPLE_COST, 0, DBL_MAX,
		NULL, NULL, NULL
	},
	{
		{"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Sets the planner's estimate of the cost of "
						 "starting up worker processes for parallel query."),
			NULL,
			GUC_EXPLAIN
		},
		&parallel_setup_cost,
		DEFAULT_PARALLEL_SETUP_COST, 0, DBL_MAX,
		NULL, NULL, NULL
	},

	{
		{"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Perform JIT compilation if query is more expensive."),
			gettext_noop("-1 disables JIT compilation."),
			GUC_EXPLAIN
		},
		&jit_above_cost,
		100000, -1, DBL_MAX,
		NULL, NULL, NULL
	},

	{
		{"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Optimize JIT-compiled functions if query is more expensive."),
			gettext_noop("-1 disables optimization."),
			GUC_EXPLAIN
		},
		&jit_optimize_above_cost,
		500000, -1, DBL_MAX,
		NULL, NULL, NULL
	},

	{
		{"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST,
			gettext_noop("Perform JIT inlining if query is more expensive."),
			gettext_noop("-1 disables inlining."),
			GUC_EXPLAIN
		},
		&jit_inline_above_cost,
		500000, -1, DBL_MAX,
		NULL, NULL, NULL
	},

	{
		{"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Sets the planner's estimate of the fraction of "
						 "a cursor's rows that will be retrieved."),
			NULL,
			GUC_EXPLAIN
		},
		&cursor_tuple_fraction,
		DEFAULT_CURSOR_TUPLE_FRACTION, 0.0, 1.0,
		NULL, NULL, NULL
	},

	{
		{"recursive_worktable_factor", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Sets the planner's estimate of the average size "
						 "of a recursive query's working table."),
			NULL,
			GUC_EXPLAIN
		},
		&recursive_worktable_factor,
		DEFAULT_RECURSIVE_WORKTABLE_FACTOR, 0.001, 1000000.0,
		NULL, NULL, NULL
	},

	{
		{"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO,
			gettext_noop("GEQO: selective pressure within the population."),
			NULL,
			GUC_EXPLAIN
		},
		&Geqo_selection_bias,
		DEFAULT_GEQO_SELECTION_BIAS,
		MIN_GEQO_SELECTION_BIAS, MAX_GEQO_SELECTION_BIAS,
		NULL, NULL, NULL
	},
	{
		{"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
			gettext_noop("GEQO: seed for random path selection."),
			NULL,
			GUC_EXPLAIN
		},
		&Geqo_seed,
		0.0, 0.0, 1.0,
		NULL, NULL, NULL
	},

	{
		{"hash_mem_multiplier", PGC_USERSET, RESOURCES_MEM,
			gettext_noop("Multiple of work_mem to use for hash tables."),
			NULL,
			GUC_EXPLAIN
		},
		&hash_mem_multiplier,
		2.0, 1.0, 1000.0,
		NULL, NULL, NULL
	},

	{
		{"bgwriter_lru_multiplier", PGC_SIGHUP, RESOURCES_BGWRITER,
			gettext_noop("Multiple of the average buffer usage to free per round."),
			NULL
		},
		&bgwriter_lru_multiplier,
		2.0, 0.0, 10.0,
		NULL, NULL, NULL
	},

	{
		{"seed", PGC_USERSET, UNGROUPED,
			gettext_noop("Sets the seed for random-number generation."),
			NULL,
			GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&phony_random_seed,
		0.0, -1.0, 1.0,
		check_random_seed, assign_random_seed, show_random_seed
	},

	{
		{"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
			gettext_noop("Vacuum cost delay in milliseconds."),
			NULL,
			GUC_UNIT_MS
		},
		&VacuumCostDelay,
		0, 0, 100,
		NULL, NULL, NULL
	},

	{
		{"autovacuum_vacuum_cost_delay", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Vacuum cost delay in milliseconds, for autovacuum."),
			NULL,
			GUC_UNIT_MS
		},
		&autovacuum_vac_cost_delay,
		2, -1, 100,
		NULL, NULL, NULL
	},

	{
		{"autovacuum_vacuum_scale_factor", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Number of tuple updates or deletes prior to vacuum as a fraction of reltuples."),
			NULL
		},
		&autovacuum_vac_scale,
		0.2, 0.0, 100.0,
		NULL, NULL, NULL
	},

	{
		{"autovacuum_vacuum_insert_scale_factor", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Number of tuple inserts prior to vacuum as a fraction of reltuples."),
			NULL
		},
		&autovacuum_vac_ins_scale,
		0.2, 0.0, 100.0,
		NULL, NULL, NULL
	},

	{
		{"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM,
			gettext_noop("Number of tuple inserts, updates, or deletes prior to analyze as a fraction of reltuples."),
			NULL
		},
		&autovacuum_anl_scale,
		0.1, 0.0, 100.0,
		NULL, NULL, NULL
	},

	{
		{"checkpoint_completion_target", PGC_SIGHUP, WAL_CHECKPOINTS,
			gettext_noop("Time spent flushing dirty buffers during checkpoint, as fraction of checkpoint interval."),
			NULL
		},
		&CheckPointCompletionTarget,
		0.9, 0.0, 1.0,
		NULL, assign_checkpoint_completion_target, NULL
	},

	{
		{"log_statement_sample_rate", PGC_SUSET, LOGGING_WHEN,
			gettext_noop("Fraction of statements exceeding log_min_duration_sample to be logged."),
			gettext_noop("Use a value between 0.0 (never log) and 1.0 (always log).")
		},
		&log_statement_sample_rate,
		1.0, 0.0, 1.0,
		NULL, NULL, NULL
	},

	{
		{"log_transaction_sample_rate", PGC_SUSET, LOGGING_WHEN,
			gettext_noop("Sets the fraction of transactions from which to log all statements."),
			gettext_noop("Use a value between 0.0 (never log) and 1.0 (log all "
						 "statements for all transactions).")
		},
		&log_xact_sample_rate,
		0.0, 0.0, 1.0,
		NULL, NULL, NULL
	},

	/* 如果我们至少找到一个分隔符，则可以 */
	{
		{NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL, NULL
	}
};


static struct config_string ConfigureNamesString[] =
{
	{
		{"archive_command", PGC_SIGHUP, WAL_ARCHIVING,
			gettext_noop("Sets the shell command that will be called to archive a WAL file."),
			gettext_noop("This is used only if \"archive_library\" is not set.")
		},
		&XLogArchiveCommand,
		"",
		NULL, NULL, fc_show_archive_command
	},

	{
		{"archive_library", PGC_SIGHUP, WAL_ARCHIVING,
			gettext_noop("Sets the library that will be called to archive a WAL file."),
			gettext_noop("An empty string indicates that \"archive_command\" should be used.")
		},
		&XLogArchiveLibrary,
		"",
		NULL, NULL, NULL
	},

	{
		{"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
			gettext_noop("Sets the shell command that will be called to retrieve an archived WAL file."),
			NULL
		},
		&recoveryRestoreCommand,
		"",
		NULL, NULL, NULL
	},

	{
		{"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
			gettext_noop("Sets the shell command that will be executed at every restart point."),
			NULL
		},
		&archiveCleanupCommand,
		"",
		NULL, NULL, NULL
	},

	{
		{"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
			gettext_noop("Sets the shell command that will be executed once at the end of recovery."),
			NULL
		},
		&recoveryEndCommand,
		"",
		NULL, NULL, NULL
	},

	{
		{"recovery_target_timeline", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Specifies the timeline to recover into."),
			NULL
		},
		&recovery_target_timeline_string,
		"latest",
		fc_check_recovery_target_timeline, fc_assign_recovery_target_timeline, NULL
	},

	{
		{"recovery_target", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Set to \"immediate\" to end recovery as soon as a consistent state is reached."),
			NULL
		},
		&recovery_target_string,
		"",
		fc_check_recovery_target, fc_assign_recovery_target, NULL
	},
	{
		{"recovery_target_xid", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Sets the transaction ID up to which recovery will proceed."),
			NULL
		},
		&recovery_target_xid_string,
		"",
		fc_check_recovery_target_xid, fc_assign_recovery_target_xid, NULL
	},
	{
		{"recovery_target_time", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Sets the time stamp up to which recovery will proceed."),
			NULL
		},
		&recovery_target_time_string,
		"",
		fc_check_recovery_target_time, fc_assign_recovery_target_time, NULL
	},
	{
		{"recovery_target_name", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Sets the named restore point up to which recovery will proceed."),
			NULL
		},
		&recovery_target_name_string,
		"",
		fc_check_recovery_target_name, fc_assign_recovery_target_name, NULL
	},
	{
		{"recovery_target_lsn", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Sets the LSN of the write-ahead log location up to which recovery will proceed."),
			NULL
		},
		&recovery_target_lsn_string,
		"",
		fc_check_recovery_target_lsn, fc_assign_recovery_target_lsn, NULL
	},

	{
		{"promote_trigger_file", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Specifies a file name whose presence ends recovery in the standby."),
			NULL
		},
		&PromoteTriggerFile,
		"",
		NULL, NULL, NULL
	},

	{
		{"primary_conninfo", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the connection string to be used to connect to the sending server."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&PrimaryConnInfo,
		"",
		NULL, NULL, NULL
	},

	{
		{"primary_slot_name", PGC_SIGHUP, REPLICATION_STANDBY,
			gettext_noop("Sets the name of the replication slot to use on the sending server."),
			NULL
		},
		&PrimarySlotName,
		"",
		fc_check_primary_slot_name, NULL, NULL
	},

	{
		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the client's character set encoding."),
			NULL,
			GUC_IS_NAME | GUC_REPORT
		},
		&client_encoding_string,
		"SQL_ASCII",
		check_client_encoding, assign_client_encoding, NULL
	},

	{
		{"log_line_prefix", PGC_SIGHUP, LOGGING_WHAT,
			gettext_noop("Controls information prefixed to each log line."),
			gettext_noop("If blank, no prefix is used.")
		},
		&Log_line_prefix,
		"%m [%p] ",
		NULL, NULL, NULL
	},

	{
		{"log_timezone", PGC_SIGHUP, LOGGING_WHAT,
			gettext_noop("Sets the time zone to use in log messages."),
			NULL
		},
		&log_timezone_string,
		"GMT",
		check_log_timezone, assign_log_timezone, show_log_timezone
	},

	{
		{"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the display format for date and time values."),
			gettext_noop("Also controls interpretation of ambiguous "
						 "date inputs."),
			GUC_LIST_INPUT | GUC_REPORT
		},
		&datestyle_string,
		"ISO, MDY",
		check_datestyle, assign_datestyle, NULL
	},

	{
		{"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the default table access method for new tables."),
			NULL,
			GUC_IS_NAME
		},
		&default_table_access_method,
		DEFAULT_TABLE_ACCESS_METHOD,
		check_default_table_access_method, NULL, NULL
	},

	{
		{"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the default tablespace to create tables and indexes in."),
			gettext_noop("An empty string selects the database's default tablespace."),
			GUC_IS_NAME
		},
		&default_tablespace,
		"",
		check_default_tablespace, NULL, NULL
	},

	{
		{"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
			NULL,
			GUC_LIST_INPUT | GUC_LIST_QUOTE
		},
		&temp_tablespaces,
		"",
		check_temp_tablespaces, assign_temp_tablespaces, NULL
	},

	{
		{"dynamic_library_path", PGC_SUSET, CLIENT_CONN_OTHER,
			gettext_noop("Sets the path for dynamically loadable modules."),
			gettext_noop("If a dynamically loadable module needs to be opened and "
						 "the specified name does not have a directory component (i.e., the "
						 "name does not contain a slash), the system will search this path for "
						 "the specified file."),
			GUC_SUPERUSER_ONLY
		},
		&Dynamic_library_path,
		"$libdir",
		NULL, NULL, NULL
	},

	{
		{"krb_server_keyfile", PGC_SIGHUP, CONN_AUTH_AUTH,
			gettext_noop("Sets the location of the Kerberos server key file."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&pg_krb_server_keyfile,
		PG_KRB_SRVTAB,
		NULL, NULL, NULL
	},

	{
		{"bonjour_name", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the Bonjour service name."),
			NULL
		},
		&bonjour_name,
		"",
		NULL, NULL, NULL
	},

	/*
	 * 在log_line_prefix可能接收非空设置之前，确保
	 * 时区处理至少处于活动状态（见elog.c）。
	 */

	{
		{"lc_collate", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the collation order locale."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&locale_collate,
		"C",
		NULL, NULL, NULL
	},

	{
		{"lc_ctype", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the character classification and case conversion locale."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&locale_ctype,
		"C",
		NULL, NULL, NULL
	},

	{
		{"lc_messages", PGC_SUSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the language in which messages are displayed."),
			NULL
		},
		&locale_messages,
		"",
		check_locale_messages, assign_locale_messages, NULL
	},

	{
		{"lc_monetary", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the locale for formatting monetary amounts."),
			NULL
		},
		&locale_monetary,
		"C",
		check_locale_monetary, assign_locale_monetary, NULL
	},

	{
		{"lc_numeric", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the locale for formatting numbers."),
			NULL
		},
		&locale_numeric,
		"C",
		check_locale_numeric, assign_locale_numeric, NULL
	},

	{
		{"lc_time", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the locale for formatting date and time values."),
			NULL
		},
		&locale_time,
		"C",
		check_locale_time, assign_locale_time, NULL
	},

	{
		{"session_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
			gettext_noop("Lists shared libraries to preload into each backend."),
			NULL,
			GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
		},
		&session_preload_libraries_string,
		"",
		NULL, NULL, NULL
	},

	{
		{"shared_preload_libraries", PGC_POSTMASTER, CLIENT_CONN_PRELOAD,
			gettext_noop("Lists shared libraries to preload into server."),
			NULL,
			GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
		},
		&shared_preload_libraries_string,
		"",
		NULL, NULL, NULL
	},

	{
		{"local_preload_libraries", PGC_USERSET, CLIENT_CONN_PRELOAD,
			gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
			NULL,
			GUC_LIST_INPUT | GUC_LIST_QUOTE
		},
		&local_preload_libraries_string,
		"",
		NULL, NULL, NULL
	},

	{
		{"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the schema search order for names that are not schema-qualified."),
			NULL,
			GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN
		},
		&namespace_search_path,
		"\"$user\", public",
		check_search_path, assign_search_path, NULL
	},

	{
		/* 应用旧GUC名称映射。 */
		{"server_encoding", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the server (database) character set encoding."),
			NULL,
			GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&server_encoding_string,
		"SQL_ASCII",
		NULL, NULL, NULL
	},

	{
		/* 应用旧GUC名称映射。 */
		{"server_version", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the server version."),
			NULL,
			GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&server_version_string,
		PG_VERSION,
		NULL, NULL, NULL
	},

	{
		/*
	 * 防止任何非交互源覆盖事务模式的尝试。
	 */
		{"role", PGC_USERSET, UNGROUPED,
			gettext_noop("Sets the current role."),
			NULL,
			GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
		},
		&role_string,
		"none",
		check_role, assign_role, show_role
	},

	{
		/* 内存不足 */
		{"session_authorization", PGC_USERSET, UNGROUPED,
			gettext_noop("Sets the session user name."),
			NULL,
			GUC_IS_NAME | GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
		},
		&session_authorization_string,
		NULL,
		check_session_authorization, assign_session_authorization, NULL
	},

	{
		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the destination for server log output."),
			gettext_noop("Valid values are combinations of \"stderr\", "
						 "\"syslog\", \"csvlog\", \"jsonlog\", and \"eventlog\", "
						 "depending on the platform."),
			GUC_LIST_INPUT
		},
		&Log_destination_string,
		"stderr",
		fc_check_log_destination, fc_assign_log_destination, NULL
	},
	{
		{"log_directory", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the destination directory for log files."),
			gettext_noop("Can be specified as relative to the data directory "
						 "or as absolute path."),
			GUC_SUPERUSER_ONLY
		},
		&Log_directory,
		"log",
		fc_check_canonical_path, NULL, NULL
	},
	{
		{"log_filename", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the file name pattern for log files."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&Log_filename,
		"postgresql-%Y-%m-%d_%H%M%S.log",
		NULL, NULL, NULL
	},

	{
		{"syslog_ident", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the program name used to identify PostgreSQL "
						 "messages in syslog."),
			NULL
		},
		&syslog_ident_str,
		"postgres",
		NULL, fc_assign_syslog_ident, NULL
	},

	{
		{"event_source", PGC_POSTMASTER, LOGGING_WHERE,
			gettext_noop("Sets the application name used to identify "
						 "PostgreSQL messages in the event log."),
			NULL
		},
		&event_source,
		DEFAULT_EVENT_SOURCE,
		NULL, NULL, NULL
	},

	{
		{"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
			NULL,
			GUC_REPORT
		},
		&timezone_string,
		"GMT",
		check_timezone, assign_timezone, show_timezone
	},
	{
		{"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Selects a file of time zone abbreviations."),
			NULL
		},
		&timezone_abbreviations_string,
		NULL,
		fc_check_timezone_abbreviations, fc_assign_timezone_abbreviations, NULL
	},

	{
		{"unix_socket_group", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the owning group of the Unix-domain socket."),
			gettext_noop("The owning user of the socket is always the user "
						 "that starts the server.")
		},
		&Unix_socket_group,
		"",
		NULL, NULL, NULL
	},

	{
		{"unix_socket_directories", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the directories where Unix-domain sockets will be created."),
			NULL,
			GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
		},
		&Unix_socket_directories,
#ifdef HAVE_UNIX_SOCKETS
		DEFAULT_PGSOCKET_DIR,
#else
		"",
#endif
		NULL, NULL, NULL
	},

	{
		{"listen_addresses", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
			gettext_noop("Sets the host name or IP address(es) to listen to."),
			NULL,
			GUC_LIST_INPUT
		},
		&ListenAddresses,
		"localhost",
		NULL, NULL, NULL
	},

	{
		/*
 * 如果在wal_consistency_checking GUC中指定了任何自定义资源管理器，
 * 则处理被推迟。现在，既然shared_preload_libraries已经加载，
 * 再次处理wal_consistency_checking。
 */
		{"data_directory", PGC_POSTMASTER, FILE_LOCATIONS,
			gettext_noop("Sets the server's data directory."),
			NULL,
			GUC_SUPERUSER_ONLY | GUC_DISALLOW_IN_AUTO_FILE
		},
		&data_directory,
		NULL,
		NULL, NULL, NULL
	},

	{
		{"config_file", PGC_POSTMASTER, FILE_LOCATIONS,
			gettext_noop("Sets the server's main configuration file."),
			NULL,
			GUC_DISALLOW_IN_FILE | GUC_SUPERUSER_ONLY
		},
		&ConfigFileName,
		NULL,
		NULL, NULL, NULL
	},

	{
		{"hba_file", PGC_POSTMASTER, FILE_LOCATIONS,
			gettext_noop("Sets the server's \"hba\" configuration file."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&HbaFileName,
		NULL,
		NULL, NULL, NULL
	},

	{
		{"ident_file", PGC_POSTMASTER, FILE_LOCATIONS,
			gettext_noop("Sets the server's \"ident\" configuration file."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&IdentFileName,
		NULL,
		NULL, NULL, NULL
	},

	{
		{"external_pid_file", PGC_POSTMASTER, FILE_LOCATIONS,
			gettext_noop("Writes the postmaster PID to the specified file."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&external_pid_file,
		NULL,
		fc_check_canonical_path, NULL, NULL
	},

	{
		{"ssl_library", PGC_INTERNAL, PRESET_OPTIONS,
			gettext_noop("Shows the name of the SSL library."),
			NULL,
			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&ssl_library,
#ifdef USE_SSL
		"OpenSSL",
#else
		"",
#endif
		NULL, NULL, NULL
	},
#ifdef FDDGM
	{
		{"ssl_enccert_file", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL server encryption certificate file."),
			NULL
		},
		&ssl_enccert_file,
		"encserver.crt",
		NULL, NULL, NULL
	},

	{
		{"ssl_enckey_file", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL server encryption private key file."),
			NULL
		},
		&ssl_enckey_file,
		"encserver.key",
		NULL, NULL, NULL
	},

#endif

	{
		{"ssl_cert_file", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL server certificate file."),
			NULL
		},
		&ssl_cert_file,
		"server.crt",
		NULL, NULL, NULL
	},

	{
		{"ssl_key_file", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL server private key file."),
			NULL
		},
		&ssl_key_file,
		"server.key",
		NULL, NULL, NULL
	},

	{
		{"ssl_ca_file", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL certificate authority file."),
			NULL
		},
		&ssl_ca_file,
		"",
		NULL, NULL, NULL
	},

	{
		{"ssl_crl_file", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL certificate revocation list file."),
			NULL
		},
		&ssl_crl_file,
		"",
		NULL, NULL, NULL
	},

	{
		{"ssl_crl_dir", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL certificate revocation list directory."),
			NULL
		},
		&ssl_crl_dir,
		"",
		NULL, NULL, NULL
	},

	{
		{"synchronous_standby_names", PGC_SIGHUP, REPLICATION_PRIMARY,
			gettext_noop("Number of synchronous standbys and list of names of potential synchronous ones."),
			NULL,
			GUC_LIST_INPUT
		},
		&SyncRepStandbyNames,
		"",
		check_synchronous_standby_names, assign_synchronous_standby_names, NULL
	},

	{
		{"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets default text search configuration."),
			NULL
		},
		&TSCurrentConfig,
		"pg_catalog.simple",
		check_TSCurrentConfig, assign_TSCurrentConfig, NULL
	},

	{
		{"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Sets the list of allowed SSL ciphers."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&SSLCipherSuites,
#ifdef USE_OPENSSL
		"HIGH:MEDIUM:+3DES:!aNULL",
#else
		"none",
#endif
		NULL, NULL, NULL
	},

	{
		{"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Sets the curve to use for ECDH."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&SSLECDHCurve,
#ifdef USE_SSL
		"prime256v1",
#else
		"none",
#endif
		NULL, NULL, NULL
	},

	{
		{"ssl_dh_params_file", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Location of the SSL DH parameters file."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&ssl_dh_params_file,
		"",
		NULL, NULL, NULL
	},

	{
		{"ssl_passphrase_command", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Command to obtain passphrases for SSL."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&ssl_passphrase_command,
		"",
		NULL, NULL, NULL
	},
#ifdef FDDGM
	{
		{"ssl_encpassphrase_command", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Command to obtain passphrases for TLCP server encryption private key."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&ssl_encpassphrase_command,
		"",
		NULL, NULL, NULL
	},
#endif
	{
		{"application_name", PGC_USERSET, LOGGING_WHAT,
			gettext_noop("Sets the application name to be reported in statistics and logs."),
			NULL,
			GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
		},
		&application_name,
		"",
		fc_check_application_name, fc_assign_application_name, NULL
	},

	{
		{"cluster_name", PGC_POSTMASTER, PROCESS_TITLE,
			gettext_noop("Sets the name of the cluster, which is included in the process title."),
			NULL,
			GUC_IS_NAME
		},
		&cluster_name,
		"",
		fc_check_cluster_name, NULL, NULL
	},

	{
		{"wal_consistency_checking", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Sets the WAL resource managers for which WAL consistency checks are done."),
			gettext_noop("Full-page images will be logged for all data blocks and cross-checked against the results of WAL replay."),
			GUC_LIST_INPUT | GUC_NOT_IN_SAMPLE
		},
		&wal_consistency_checking_string,
		"",
		fc_check_wal_consistency_checking, fc_assign_wal_consistency_checking, NULL
	},

	{
		{"jit_provider", PGC_POSTMASTER, CLIENT_CONN_PRELOAD,
			gettext_noop("JIT provider to use."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&jit_provider,
		"llvmjit",
		NULL, NULL, NULL
	},

	{
		{"backtrace_functions", PGC_SUSET, DEVELOPER_OPTIONS,
			gettext_noop("Log backtrace for errors in these functions."),
			NULL,
			GUC_NOT_IN_SAMPLE
		},
		&backtrace_functions,
		"",
		fc_check_backtrace_functions, fc_assign_backtrace_functions, NULL
	},

	{
		{"restrict_nonsystem_relation_kind", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Prohibits access to non-system relations of specified kinds."),
			NULL,
			GUC_LIST_INPUT | GUC_NOT_IN_SAMPLE
		},
		&restrict_nonsystem_relation_kind_string,
		"",
		check_restrict_nonsystem_relation_kind, assign_restrict_nonsystem_relation_kind, NULL
	},
#ifdef FDD
	{
		{"encryption_key_command", PGC_POSTMASTER, 0,
			gettext_noop("Sets the shell command that will be called to fetch database encryption key."),
			NULL,
			GUC_NO_SHOW_ALL | GUC_SUPERUSER_ONLY | GUC_DISALLOW_IN_AUTO_FILE
		},
		&g_fdbEncryptionKeyCommand,
		NULL,
		NULL, NULL, fc_FDB_showEncryptionKeyCommand
	},
#endif

	/* 如果我们至少找到一个分隔符，则可以 */
	{
		{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
	}
};


static struct config_enum ConfigureNamesEnum[] =
{
	{
		{"backslash_quote", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
			gettext_noop("Sets whether \"\\'\" is allowed in string literals."),
			NULL
		},
		&backslash_quote,
		BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options,
		NULL, NULL, NULL
	},

	{
		{"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the output format for bytea."),
			NULL
		},
		&bytea_output,
		BYTEA_OUTPUT_HEX, bytea_output_options,
		NULL, NULL, NULL
	},

	{
		{"client_min_messages", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the message levels that are sent to the client."),
			gettext_noop("Each level includes all the levels that follow it. The later"
						 " the level, the fewer messages are sent.")
		},
		&client_min_messages,
		NOTICE, client_message_level_options,
		NULL, NULL, NULL
	},

	{
		{"compute_query_id", PGC_SUSET, STATS_MONITORING,
			gettext_noop("Enables in-core computation of query identifiers."),
			NULL
		},
		&compute_query_id,
		COMPUTE_QUERY_ID_AUTO, compute_query_id_options,
		NULL, NULL, NULL
	},

	{
		{"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Enables the planner to use constraints to optimize queries."),
			gettext_noop("Table scans will be skipped if their constraints"
						 " guarantee that no rows match the query."),
			GUC_EXPLAIN
		},
		&constraint_exclusion,
		CONSTRAINT_EXCLUSION_PARTITION, constraint_exclusion_options,
		NULL, NULL, NULL
	},

	{
		{"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the default compression method for compressible values."),
			NULL
		},
		&default_toast_compression,
		TOAST_PGLZ_COMPRESSION,
		default_toast_compression_options,
		NULL, NULL, NULL
	},

	{
		{"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the transaction isolation level of each new transaction."),
			NULL
		},
		&DefaultXactIsoLevel,
		XACT_READ_COMMITTED, isolation_level_options,
		NULL, NULL, NULL
	},

	{
		{"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the current transaction's isolation level."),
			NULL,
			GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
		},
		&XactIsoLevel,
		XACT_READ_COMMITTED, isolation_level_options,
		check_XactIsoLevel, NULL, NULL
	},

	{
		{"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
			gettext_noop("Sets the display format for interval values."),
			NULL,
			GUC_REPORT
		},
		&IntervalStyle,
		INTSTYLE_POSTGRES, intervalstyle_options,
		NULL, NULL, NULL
	},

	{
		{"log_error_verbosity", PGC_SUSET, LOGGING_WHAT,
			gettext_noop("Sets the verbosity of logged messages."),
			NULL
		},
		&Log_error_verbosity,
		PGERROR_DEFAULT, log_error_verbosity_options,
		NULL, NULL, NULL
	},

	{
		{"log_min_messages", PGC_SUSET, LOGGING_WHEN,
			gettext_noop("Sets the message levels that are logged."),
			gettext_noop("Each level includes all the levels that follow it. The later"
						 " the level, the fewer messages are sent.")
		},
		&log_min_messages,
		WARNING, server_message_level_options,
		NULL, NULL, NULL
	},

	{
		{"log_min_error_statement", PGC_SUSET, LOGGING_WHEN,
			gettext_noop("Causes all statements generating error at or above this level to be logged."),
			gettext_noop("Each level includes all the levels that follow it. The later"
						 " the level, the fewer messages are sent.")
		},
		&log_min_error_statement,
		ERROR, server_message_level_options,
		NULL, NULL, NULL
	},

	{
		{"log_statement", PGC_SUSET, LOGGING_WHAT,
			gettext_noop("Sets the type of statements logged."),
			NULL
		},
		&log_statement,
		LOGSTMT_NONE, log_statement_options,
		NULL, NULL, NULL
	},

	{
		{"syslog_facility", PGC_SIGHUP, LOGGING_WHERE,
			gettext_noop("Sets the syslog \"facility\" to be used when syslog enabled."),
			NULL
		},
		&syslog_facility,
#ifdef HAVE_SYSLOG
		LOG_LOCAL0,
#else
		0,
#endif
		syslog_facility_options,
		NULL, fc_assign_syslog_facility, NULL
	},

	{
		{"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the session's behavior for triggers and rewrite rules."),
			NULL
		},
		&SessionReplicationRole,
		SESSION_REPLICATION_ROLE_ORIGIN, session_replication_role_options,
		NULL, fc_assign_session_replication_role, NULL
	},

	{
		{"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
			gettext_noop("Sets the current transaction's synchronization level."),
			NULL
		},
		&synchronous_commit,
		SYNCHRONOUS_COMMIT_ON, synchronous_commit_options,
		NULL, assign_synchronous_commit, NULL
	},

	{
		{"archive_mode", PGC_POSTMASTER, WAL_ARCHIVING,
			gettext_noop("Allows archiving of WAL files using archive_command."),
			NULL
		},
		&XLogArchiveMode,
		ARCHIVE_MODE_OFF, archive_mode_options,
		NULL, NULL, NULL
	},

	{
		{"recovery_target_action", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
			gettext_noop("Sets the action to perform upon reaching the recovery target."),
			NULL
		},
		&recoveryTargetAction,
		RECOVERY_TARGET_ACTION_PAUSE, recovery_target_action_options,
		NULL, NULL, NULL
	},

	{
		{"trace_recovery_messages", PGC_SIGHUP, DEVELOPER_OPTIONS,
			gettext_noop("Enables logging of recovery-related debugging information."),
			gettext_noop("Each level includes all the levels that follow it. The later"
						 " the level, the fewer messages are sent."),
			GUC_NOT_IN_SAMPLE,
		},
		&trace_recovery_messages,

		/*
 * 分配可以来自服务器环境的任何GUC值。
 *
 * 这从InitializeGUCOptions调用，并且也从ProcessConfigFile调用，
 * 以处理设置已从postgresql.conf中删除并且现在应从环境中获取值的可能性。
 * （后者是一种临时解决方法，可能应该在未来去掉；如果是这样，
 * 将其合并回InitializeGUCOptions。）
 */
		LOG, client_message_level_options,
		NULL, NULL, NULL
	},

	{
		{"track_functions", PGC_SUSET, STATS_CUMULATIVE,
			gettext_noop("Collects function-level statistics on database activity."),
			NULL
		},
		&pgstat_track_functions,
		TRACK_FUNC_OFF, track_function_options,
		NULL, NULL, NULL
	},


	{
		{"stats_fetch_consistency", PGC_USERSET, STATS_CUMULATIVE,
			gettext_noop("Sets the consistency of accesses to statistics data."),
			NULL
		},
		&pgstat_fetch_consistency,
		PGSTAT_FETCH_CONSISTENCY_CACHE, stats_fetch_consistency,
		NULL, assign_stats_fetch_consistency, NULL
	},

	{
		{"wal_compression", PGC_SUSET, WAL_SETTINGS,
			gettext_noop("Compresses full-page writes written in WAL file with specified method."),
			NULL
		},
		&wal_compression,
		WAL_COMPRESSION_NONE, wal_compression_options,
		NULL, NULL, NULL
	},

	{
		{"wal_level", PGC_POSTMASTER, WAL_SETTINGS,
			gettext_noop("Sets the level of information written to the WAL."),
			NULL
		},
		&wal_level,
		WAL_LEVEL_REPLICA, wal_level_options,
		NULL, NULL, NULL
	},

	{
		{"dynamic_shared_memory_type", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("Selects the dynamic shared memory implementation used."),
			NULL
		},
		&dynamic_shared_memory_type,
		DEFAULT_DYNAMIC_SHARED_MEMORY_TYPE, dynamic_shared_memory_options,
		NULL, NULL, NULL
	},

	{
		{"shared_memory_type", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("Selects the shared memory implementation used for the main shared memory region."),
			NULL
		},
		&shared_memory_type,
		DEFAULT_SHARED_MEMORY_TYPE, shared_memory_options,
		NULL, NULL, NULL
	},

	{
		{"wal_sync_method", PGC_SIGHUP, WAL_SETTINGS,
			gettext_noop("Selects the method used for forcing WAL updates to disk."),
			NULL
		},
		&sync_method,
		DEFAULT_SYNC_METHOD, sync_method_options,
		NULL, assign_xlog_sync_method, NULL
	},

	{
		{"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets how binary values are to be encoded in XML."),
			NULL
		},
		&xmlbinary,
		XMLBINARY_BASE64, xmlbinary_options,
		NULL, NULL, NULL
	},

	{
		{"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets whether XML data in implicit parsing and serialization "
						 "operations is to be considered as documents or content fragments."),
			NULL
		},
		&xmloption,
		XMLOPTION_CONTENT, xmloption_options,
		NULL, NULL, NULL
	},

	{
		{"huge_pages", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("Use of huge pages on Linux or Windows."),
			NULL
		},
		&huge_pages,
		HUGE_PAGES_TRY, huge_pages_options,
		NULL, NULL, NULL
	},

	{
		{"recovery_prefetch", PGC_SIGHUP, WAL_RECOVERY,
			gettext_noop("Prefetch referenced blocks during recovery."),
			gettext_noop("Look ahead in the WAL to find references to uncached data.")
		},
		&recovery_prefetch,
		RECOVERY_PREFETCH_TRY, recovery_prefetch_options,
		check_recovery_prefetch, assign_recovery_prefetch, NULL
	},

	{
		{"force_parallel_mode", PGC_USERSET, DEVELOPER_OPTIONS,
			gettext_noop("Forces use of parallel query facilities."),
			gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."),
			GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
		},
		&force_parallel_mode,
		FORCE_PARALLEL_OFF, force_parallel_mode_options,
		NULL, NULL, NULL
	},

	{
		{"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
			gettext_noop("Chooses the algorithm for encrypting passwords."),
			NULL
		},
		&Password_encryption,
		PASSWORD_TYPE_SCRAM_SHA_256, password_encryption_options,
		NULL, NULL, NULL
	},

	{
		{"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
			gettext_noop("Controls the planner's selection of custom or generic plan."),
			gettext_noop("Prepared statements can have custom and generic plans, and the planner "
						 "will attempt to choose which is better.  This can be set to override "
						 "the default behavior."),
			GUC_EXPLAIN
		},
		&plan_cache_mode,
		PLAN_CACHE_MODE_AUTO, plan_cache_mode_options,
		NULL, NULL, NULL
	},

	{
		{"ssl_min_protocol_version", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Sets the minimum SSL/TLS protocol version to use."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&ssl_min_protocol_version,
		PG_TLS1_2_VERSION,
		ssl_protocol_versions_info + 1, /*
	 * rlimit并不完全是一个“环境变量”，但其行为大致相同。
	 * 如果我们可以识别平台的堆栈深度rlimit，则将默认堆栈深度设置增加到
	 * 安全的最大值（但最多2MB）。
	 * 如果值为2MB，则将其源报告为PGC_S_DYNAMIC_DEFAULT，或者
	 * 如果反映rlimit限制，则报告为PGC_S_ENV_VAR。
	 */
		NULL, NULL, NULL
	},

	{
		{"ssl_max_protocol_version", PGC_SIGHUP, CONN_AUTH_SSL,
			gettext_noop("Sets the maximum SSL/TLS protocol version to use."),
			NULL,
			GUC_SUPERUSER_ONLY
		},
		&ssl_max_protocol_version,
		PG_TLS_ANY,
		ssl_protocol_versions_info,
		NULL, NULL, NULL
	},

	{
		{"recovery_init_sync_method", PGC_SIGHUP, ERROR_HANDLING_OPTIONS,
			gettext_noop("Sets the method for synchronizing the data directory before crash recovery."),
		},
		&recovery_init_sync_method,
		RECOVERY_INIT_SYNC_METHOD_FSYNC, recovery_init_sync_method_options,
		NULL, NULL, NULL
	},
#ifdef FDD
	{
		{"tde_encrypt", PGC_SIGHUP, UNGROUPED,		
			gettext_noop("Set TDE encrypt type (plain/rc4/sm4)."),
			NULL,
			GUC_NO_SHOW_ALL
		},
		&g_tde_encrypt,
		FDB_ENCRYPT_TYPE_NONE, tde_encrypt_method_options,
		fc_FDB_checkTDEEncrpt, fc_FDB_assignTDEEncrpt, NULL
	},

#endif

	/* 如果我们至少找到一个分隔符，则可以 */
	{
		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
	}
};

/* 非NULL的boot_val必须始终进行strdup */


/*
 * 选择要使用的配置文件和数据目录，并
 * 进行 postgresql.conf 的初始读取。
 *
 * 在处理命令行开关后调用此函数。
 *		userDoption 是 -D 开关值（如果指定，则为 NULL）。
 *		progname 仅用于错误信息中。
 *
 * 成功时返回 true；失败时，将适当的错误信息打印到 stderr 并返回 false。
 */
static const char *const map_old_guc_names[] = {
	"sort_mem", "work_mem",
	"vacuum_mem", "maintenance_work_mem",
	NULL
};


/* configdir 是 -D 选项，或 $PGDATA 如果没有 -D */
static struct config_generic **guc_variables;

/*
	 * 查找配置文件：如果在命令行中指定了 config_file，使用它，否则使用 configdir/postgresql.conf。无论如何
	 * 确保结果是绝对路径，以便将来后端能够以同样的方式解释它。
	 */
static int	num_guc_variables;

/*
	 * 将 ConfigFileName GUC 变量设置为其最终值，确保后续不可被覆盖。
	 */
static int	size_guc_variables;


static bool guc_dirty;			/*
	 * 现在第一次读取配置文件。
	 */

static bool reporting_enabled;	/*
	 * 第一次读取配置文件。这次只获取 data_directory 参数以确定数据目录，
	 * 以便下次我们可以读取 PG_AUTOCONF_FILENAME 文件。
	 */

static bool report_needed;		/*
	 * 如果已设置 data_directory GUC 变量，则将其用作 DataDir；
	 * 否则如果设置了 configdir，则使用它；否则放弃。
	 *
	 * 注意：SetDataDir 将复制并绝对化其参数，因此我们不需要这样做。
	 */

static int	GUCNestLevel = 0;	/*
	 * 将最终的 DataDir 值反映回 data_directory GUC 变量中。
	 * （如果你在想为什么我们不将它们做成一个变量，
	 * 是因为 EXEC_BACKEND 情况需要特别传递 DataDir 给子后端。
	 * XXX 这仍然如此吗？考虑到我们现在
	 * 已经更改到 DataDir，EXEC_BACKEND 可以在不知道
	 * DataDir 的前提下读取配置文件。）
	 */


static int	fc_guc_var_compare(const void *fc_a, const void *fc_b);
static int	guc_name_compare(const char *fc_namea, const char *fc_nameb);
static void InitializeGUCOptionsFromEnvironment(void);
static void fc_InitializeOneGUCOption(struct config_generic *fc_gconf);
static void fc_push_old_value(struct config_generic *fc_gconf, GucAction fc_action);
static void fc_ReportGUCOption(struct config_generic *fc_record);
static void fc_reapply_stacked_values(struct config_generic *fc_variable,
								   struct config_string *fc_pHolder,
								   GucStack *fc_stack,
								   const char *fc_curvalue,
								   GucContext fc_curscontext, GucSource fc_cursource,
								   Oid fc_cursrole);
static void fc_ShowGUCConfigOption(const char *fc_name, DestReceiver *fc_dest);
static void fc_ShowAllGUCConfig(DestReceiver *fc_dest);
static char *fc_ShowOption(struct config_generic *fc_record, bool fc_use_units);
static bool fc_validate_option_array_item(const char *fc_name, const char *fc_value,
									   bool fc_skipIfNoPermissions);
static void fc_write_auto_conf_file(int fc_fd, const char *fc_filename, ConfigVariable *fc_head_p);
static void fc_replace_auto_config_value(ConfigVariable **fc_head_p, ConfigVariable **fc_tail_p,
									  const char *fc_name, const char *fc_value);


/*
	 * 现在第二次读取配置文件，允许 PG_AUTOCONF_FILENAME 文件中的任何设置生效。
	 * （这很丑，但由于我们必须在找到 autoconf
	 * 文件之前确定 DataDir，其他选择似乎更糟。）
	 */
static void * fc_guc_malloc(int fc_elevel, size_t fc_size)
{
	void	   *fc_data;

	/* 避免 malloc(0) 的不可移植行为 */
	if (fc_size == 0)
		fc_size = 1;
	fc_data = malloc(fc_size);
	if (fc_data == NULL)
		ereport(fc_elevel,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));
	return fc_data;
}

static void * fc_guc_realloc(int fc_elevel, void *fc_old, size_t fc_size)
{
	void	   *fc_data;

	/* 避免 realloc(NULL, 0) 的不可移植行为 */
	if (fc_old == NULL && fc_size == 0)
		fc_size = 1;
	fc_data = realloc(fc_old, fc_size);
	if (fc_data == NULL)
		ereport(fc_elevel,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));
	return fc_data;
}

static char * fc_guc_strdup(int fc_elevel, const char *fc_src)
{
	char	   *fc_data;

	fc_data = strdup(fc_src);
	if (fc_data == NULL)
		ereport(fc_elevel,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));
	return fc_data;
}


/*
	 * pg_ident.conf 同样处理。
	 */
static bool fc_string_field_used(struct config_string *fc_conf, char *fc_strval)
{
	GucStack   *fc_stack;

	if (fc_strval == *(fc_conf->variable) ||
		fc_strval == fc_conf->reset_val ||
		fc_strval == fc_conf->boot_val)
		return true;
	for (fc_stack = fc_conf->gen.stack; fc_stack; fc_stack = fc_stack->prev)
	{
		if (fc_strval == fc_stack->prior.val.stringval ||
			fc_strval == fc_stack->masked.val.stringval)
			return true;
	}
	return false;
}

/*
 * 将所有选项重置为其保存的默认值（实现 RESET ALL）
 */
static void fc_set_string_field(struct config_string *fc_conf, char **fc_field, char *fc_newval)
{
	char	   *fc_oldval = *fc_field;

	/* 不重置不可设置的值 */
	*fc_field = fc_newval;

	/* 如果特殊排除在 RESET ALL 之外，则不要重置 */
	if (fc_oldval && !fc_string_field_used(fc_conf, fc_oldval))
		free(fc_oldval);
}

/* 如果未设置，则无需重置 */
static bool fc_extra_field_used(struct config_generic *fc_gconf, void *fc_extra)
{
	GucStack   *fc_stack;

	if (fc_extra == fc_gconf->extra)
		return true;
	switch (fc_gconf->vartype)
	{
		case PGC_BOOL:
			if (fc_extra == ((struct config_bool *) fc_gconf)->reset_extra)
				return true;
			break;
		case PGC_INT:
			if (fc_extra == ((struct config_int *) fc_gconf)->reset_extra)
				return true;
			break;
		case PGC_REAL:
			if (fc_extra == ((struct config_real *) fc_gconf)->reset_extra)
				return true;
			break;
		case PGC_STRING:
			if (fc_extra == ((struct config_string *) fc_gconf)->reset_extra)
				return true;
			break;
		case PGC_ENUM:
			if (fc_extra == ((struct config_enum *) fc_gconf)->reset_extra)
				return true;
			break;
	}
	for (fc_stack = fc_gconf->stack; fc_stack; fc_stack = fc_stack->prev)
	{
		if (fc_extra == fc_stack->prior.extra ||
			fc_extra == fc_stack->masked.extra)
			return true;
	}

	return false;
}

/* 保存旧值以支持事务回滚 */
static void fc_set_extra_field(struct config_generic *fc_gconf, void **fc_field, void *fc_newval)
{
	void	   *fc_oldval = *fc_field;

	/* 不重置不可设置的值 */
	*fc_field = fc_newval;

	/* 如果特殊排除在 RESET ALL 之外，则不要重置 */
	if (fc_oldval && !fc_extra_field_used(fc_gconf, fc_oldval))
		free(fc_oldval);
}

/* 我们是否已经有当前嵌套级别的栈条目？ */
static void fc_set_stack_value(struct config_generic *fc_gconf, config_var_value *fc_val)
{
	switch (fc_gconf->vartype)
	{
		case PGC_BOOL:
			fc_val->val.boolval =
				*((struct config_bool *) fc_gconf)->variable;
			break;
		case PGC_INT:
			fc_val->val.intval =
				*((struct config_int *) fc_gconf)->variable;
			break;
		case PGC_REAL:
			fc_val->val.realval =
				*((struct config_real *) fc_gconf)->variable;
			break;
		case PGC_STRING:
			fc_set_string_field((struct config_string *) fc_gconf,
							 &(fc_val->val.stringval),
							 *((struct config_string *) fc_gconf)->variable);
			break;
		case PGC_ENUM:
			fc_val->val.enumval =
				*((struct config_enum *) fc_gconf)->variable;
			break;
	}
	fc_set_extra_field(fc_gconf, &(fc_val->extra), fc_gconf->extra);
}

/* 是的，因此如果有必要，调整其状态 */
static void fc_discard_stack_value(struct config_generic *fc_gconf, config_var_value *fc_val)
{
	switch (fc_gconf->vartype)
	{
		case PGC_BOOL:
		case PGC_INT:
		case PGC_REAL:
		case PGC_ENUM:
			/* 不需要做任何事情 */
			break;
		case PGC_STRING:
			fc_set_string_field((struct config_string *) fc_gconf,
							 &(fc_val->val.stringval),
							 NULL);
			break;
	}
	fc_set_extra_field(fc_gconf, &(fc_val->extra), NULL);
}


/* 必须丢弃旧的屏蔽值 */
struct config_generic **
get_guc_variables(void)
{
	return guc_variables;
}


/* SET 后跟 SET LOCAL，记住 SET 的值 */
void build_guc_variables(void)
{
	int			fc_size_vars;
	int			fc_num_vars = 0;
	struct config_generic **fc_guc_vars;
	int			fc_i;

	for (fc_i = 0; ConfigureNamesBool[fc_i].gen.name; fc_i++)
	{
		struct config_bool *fc_conf = &ConfigureNamesBool[fc_i];

		/* 在所有其他情况下，不更改栈条目 */
		fc_conf->gen.vartype = PGC_BOOL;
		fc_num_vars++;
	}

	for (fc_i = 0; ConfigureNamesInt[fc_i].gen.name; fc_i++)
	{
		struct config_int *fc_conf = &ConfigureNamesInt[fc_i];

		fc_conf->gen.vartype = PGC_INT;
		fc_num_vars++;
	}

	for (fc_i = 0; ConfigureNamesReal[fc_i].gen.name; fc_i++)
	{
		struct config_real *fc_conf = &ConfigureNamesReal[fc_i];

		fc_conf->gen.vartype = PGC_REAL;
		fc_num_vars++;
	}

	for (fc_i = 0; ConfigureNamesString[fc_i].gen.name; fc_i++)
	{
		struct config_string *fc_conf = &ConfigureNamesString[fc_i];

		fc_conf->gen.vartype = PGC_STRING;
		fc_num_vars++;
	}

	for (fc_i = 0; ConfigureNamesEnum[fc_i].gen.name; fc_i++)
	{
		struct config_enum *fc_conf = &ConfigureNamesEnum[fc_i];

		fc_conf->gen.vartype = PGC_ENUM;
		fc_num_vars++;
	}

	/* 只能有同一变量的先前 SAVE */
	fc_size_vars = fc_num_vars + fc_num_vars / 4;

	fc_guc_vars = (struct config_generic **)
		fc_guc_malloc(FATAL, fc_size_vars * sizeof(struct config_generic *));

	fc_num_vars = 0;

	for (fc_i = 0; ConfigureNamesBool[fc_i].gen.name; fc_i++)
		fc_guc_vars[fc_num_vars++] = &ConfigureNamesBool[fc_i].gen;

	for (fc_i = 0; ConfigureNamesInt[fc_i].gen.name; fc_i++)
		fc_guc_vars[fc_num_vars++] = &ConfigureNamesInt[fc_i].gen;

	for (fc_i = 0; ConfigureNamesReal[fc_i].gen.name; fc_i++)
		fc_guc_vars[fc_num_vars++] = &ConfigureNamesReal[fc_i].gen;

	for (fc_i = 0; ConfigureNamesString[fc_i].gen.name; fc_i++)
		fc_guc_vars[fc_num_vars++] = &ConfigureNamesString[fc_i].gen;

	for (fc_i = 0; ConfigureNamesEnum[fc_i].gen.name; fc_i++)
		fc_guc_vars[fc_num_vars++] = &ConfigureNamesEnum[fc_i].gen;

	if (guc_variables)
		free(guc_variables);
	guc_variables = fc_guc_vars;
	num_guc_variables = fc_num_vars;
	size_guc_variables = fc_size_vars;
	qsort((void *) guc_variables, num_guc_variables,
		  sizeof(struct config_generic *), fc_guc_var_compare);
}

/* 必须已经设置 */
static bool fc_add_guc_variable(struct config_generic *fc_var, int fc_elevel)
{
	if (num_guc_variables + 1 >= size_guc_variables)
	{
		/*
	 * 推入一个新的栈条目
	 *
	 * 为简单起见，我们将所有栈条目保存在 TopTransactionContext 中。
	 */
		int			fc_size_vars = size_guc_variables + size_guc_variables / 4;
		struct config_generic **fc_guc_vars;

		if (fc_size_vars == 0)
		{
			fc_size_vars = 100;
			fc_guc_vars = (struct config_generic **)
				fc_guc_malloc(fc_elevel, fc_size_vars * sizeof(struct config_generic *));
		}
		else
		{
			fc_guc_vars = (struct config_generic **)
				fc_guc_realloc(fc_elevel, guc_variables, fc_size_vars * sizeof(struct config_generic *));
		}

		if (fc_guc_vars == NULL)
			return false;		/* 内存不足 */

		guc_variables = fc_guc_vars;
		size_guc_variables = fc_size_vars;
	}
	guc_variables[num_guc_variables++] = fc_var;
	qsort((void *) guc_variables, num_guc_variables,
		  sizeof(struct config_generic *), fc_guc_var_compare);
	return true;
}

/*
 * 在主事务开始时进行 GUC 处理。
 */
static bool valid_custom_variable_name(const char *fc_name)
{
	bool		fc_saw_sep = false;
	bool		fc_name_start = true;

	for (const char *fc_p = fc_name; *fc_p; fc_p++)
	{
		if (*fc_p == GUC_QUALIFIER_SEPARATOR)
		{
			if (fc_name_start)
				return false;	/*
	 * 在事务之间嵌套级别应为 0；如果不是，则说明某人
	 * 没有调用 AtEOXact_GUC，或以错误的 nestLevel 调用了它。我们
	 * 发出警告，但不做其他清理工作。
	 */
			fc_saw_sep = true;
			fc_name_start = true;
		}
		else if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
						"abcdefghijklmnopqrstuvwxyz_", *fc_p) != NULL ||
				 IS_HIGHBIT_SET(*fc_p))
		{
			/*
 * 为 GUC 值进入一个新的嵌套级别。这是在子事务
 * 开始时调用的，当进入具有 proconfig 设置的函数时，以及在
 * 一些其他地方，我们想临时设置 GUC 变量。
 * 注意：我们必须避免在此处出现错误，否则子事务开始时将会很不愉快。
 */
			fc_name_start = false;
		}
		else if (!fc_name_start && strchr("0123456789$", *fc_p) != NULL)
			 /*
 * 在事务或子事务提交或中止时进行 GUC 处理，或
 * 当退出具有 proconfig 设置的函数时，或在撤消
 * 对某些 GUC 变量的临时赋值时进行处理。 （因此这个名称有点不准确；
 * 也许它应该是 ExitGUCNestLevel 或类似的名字。）
 * 在中止期间，我们丢弃在嵌套
 * 级别 >= nestLevel 应用的所有 GUC 设置。 nestLevel == 1 对应于主事务。
 */ ;
		else
			return false;
	}
	if (fc_name_start)
		return false;			/*
	 * 在事务之间嵌套级别应为 0；如果不是，则说明某人
	 * 没有调用 AtEOXact_GUC，或以错误的 nestLevel 调用了它。我们
	 * 发出警告，但不做其他清理工作。
	 */
	/* 如果在此事务中没有更改，则快速退出 */
	return fc_saw_sep;
}

/*
		 * 处理并弹出嵌套级别内的每个栈条目。为了简化
		 * fmgr_security_definer() 和使用 GUC_ACTION_SAVE 的其他地方，
		 * 我们允许使用本地嵌套级别的代码在周围的事务或子事务中止时恢复失败退出；
		 * 因此，可能有多个栈条目需要弹出。
		 */
static struct config_generic * fc_add_placeholder_variable(const char *fc_name, int fc_elevel)
{
	size_t		fc_sz = sizeof(struct config_string) + sizeof(char *);
	struct config_string *fc_var;
	struct config_generic *fc_gen;

	fc_var = (struct config_string *) fc_guc_malloc(fc_elevel, fc_sz);
	if (fc_var == NULL)
		return NULL;
	memset(fc_var, 0, fc_sz);
	fc_gen = &fc_var->gen;

	fc_gen->name = fc_guc_strdup(fc_elevel, fc_name);
	if (fc_gen->name == NULL)
	{
		free(fc_var);
		return NULL;
	}

	fc_gen->context = PGC_USERSET;
	fc_gen->group = CUSTOM_OPTIONS;
	fc_gen->short_desc = "GUC placeholder variable";
	fc_gen->flags = GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_CUSTOM_PLACEHOLDER;
	fc_gen->vartype = PGC_STRING;

	/*
			 * 在接下来的部分中，如果我们不设置 restorePrior 或
			 * restoreMasked，则必须“丢弃”栈条目的任何不需要的字段以避免内存泄漏。
			 * 如果我们设置其中一个标志，未使用的字段将在恢复后被清理。
			 */
	fc_var->variable = (char **) (fc_var + 1);

	if (!fc_add_guc_variable((struct config_generic *) fc_var, fc_elevel))
	{
		free(unconstify(char *, fc_gen->name));
		free(fc_var);
		return NULL;
	}

	return fc_gen;
}

/* 如果中止，始终恢复先前的值 */
static struct config_generic * find_option(const char *fc_name, bool fc_create_placeholders, bool fc_skip_errors,
			int fc_elevel)
{
	const char **fc_key = &fc_name;
	struct config_generic **fc_res;
	int			fc_i;

	Assert(fc_name);

	/* 事务提交 */
	fc_res = (struct config_generic **) bsearch((void *) &fc_key,
											 (void *) guc_variables,
											 num_guc_variables,
											 sizeof(struct config_generic *),
											 fc_guc_var_compare);
	if (fc_res)
		return *fc_res;

	/* 我们保留当前活动值 */
	for (fc_i = 0; map_old_guc_names[fc_i] != NULL; fc_i += 2)
	{
		if (guc_name_compare(fc_name, map_old_guc_names[fc_i]) == 0)
			return find_option(map_old_guc_names[fc_i + 1], false,
							   fc_skip_errors, fc_elevel);
	}

	if (fc_create_placeholders)
	{
		/* 必须是 GUC_LOCAL */
		const char *fc_sep = strchr(fc_name, GUC_QUALIFIER_SEPARATOR);

		if (fc_sep != NULL)
		{
			size_t		fc_classLen = fc_sep - fc_name;
			ListCell   *fc_lc;

			/* 递减条目的级别并且不弹出它 */
			if (!valid_custom_variable_name(fc_name))
			{
				if (!fc_skip_errors)
					ereport(fc_elevel,
							(errcode(ERRCODE_INVALID_NAME),
							 errmsg("invalid configuration parameter name \"%s\"",
									fc_name),
							 errdetail("Custom parameter names must be two or more simple identifiers separated by dots.")));
				return NULL;
			}
			/*
				 * 我们必须将这个栈条目合并到之前的条目中。有关此部分的讨论，请参见 README。
				 */
			foreach(fc_lc, reserved_class_prefix)
			{
				const char *fc_rcprefix = lfirst(fc_lc);

				if (strlen(fc_rcprefix) == fc_classLen &&
					strncmp(fc_name, fc_rcprefix, fc_classLen) == 0)
				{
					if (!fc_skip_errors)
						ereport(fc_elevel,
								(errcode(ERRCODE_INVALID_NAME),
								 errmsg("invalid configuration parameter name \"%s\"",
										fc_name),
								 errdetail("\"%s\" is a reserved prefix.",
										   fc_rcprefix)));
					return NULL;
				}
			}
			/* 不能到这里 */
			return fc_add_placeholder_variable(fc_name, fc_elevel);
		}
	}

	/* 下一个级别总是变为 SET */
	if (!fc_skip_errors)
		ereport(fc_elevel,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("unrecognized configuration parameter \"%s\"",
						fc_name)));
	return NULL;
}


/* LOCAL 迁移下去 */
static int fc_guc_var_compare(const void *fc_a, const void *fc_b)
{
	const char *fc_namea = **(const char **const *) fc_a;
	const char *fc_nameb = **(const char **const *) fc_b;

	return guc_name_compare(fc_namea, fc_nameb);
}

/* 否则只是忘记这个栈级别 */
static int guc_name_compare(const char *fc_namea, const char *fc_nameb)
{
	/* 此级别的先前状态不再需要 */
	while (*fc_namea && *fc_nameb)
	{
		char		fc_cha = *fc_namea++;
		char		fc_chb = *fc_nameb++;

		if (fc_cha >= 'A' && fc_cha <= 'Z')
			fc_cha += 'a' - 'A';
		if (fc_chb >= 'A' && fc_chb <= 'Z')
			fc_chb += 'a' - 'A';
		if (fc_cha != fc_chb)
			return fc_cha - fc_chb;
	}
	if (*fc_namea)
		return 1;				/* 复制下被屏蔽的状态 */
	if (*fc_nameb)
		return -1;				/* 执行适当的堆栈值恢复 */
	return 0;
}


/*
    * 如果不再使用，则释放堆栈值。我们可以在这里使用 discard_stack_value()，但由于我们有特定类型的代码，还是直接内联比较好。
    */
char * convert_GUC_name_for_parameter_acl(const char *fc_name)
{
	char	   *fc_result;

	/*
    * 如果不再使用，则释放堆栈中的额外值。
    */
	for (int fc_i = 0; map_old_guc_names[fc_i] != NULL; fc_i += 2)
	{
		if (guc_name_compare(fc_name, map_old_guc_names[fc_i]) == 0)
		{
			fc_name = map_old_guc_names[fc_i + 1];
			break;
		}
	}

	/* 并恢复源信息 */
	fc_result = pstrdup(fc_name);
	for (char *fc_ptr = fc_result; *fc_ptr != '\0'; fc_ptr++)
	{
		char		fc_ch = *fc_ptr;

		if (fc_ch >= 'A' && fc_ch <= 'Z')
		{
			fc_ch += 'a' - 'A';
			*fc_ptr = fc_ch;
		}
	}

	return fc_result;
}

/* 完成弹出状态堆栈 */
bool check_GUC_name_for_parameter_acl(const char *fc_name)
{
	/* 如果我们改变了它，则报告新值 */
	if (find_option(fc_name, false, true, DEBUG1) != NULL)
		return true;
	/* 堆栈弹出循环结束 */
	if (valid_custom_variable_name(fc_name))
		return true;
	return false;
}


/* 如果没有剩余的堆栈条目，我们可以重置 guc_dirty */
void InitializeGUCOptions(void)
{
	int			fc_i;

	/* 更新嵌套级别 */
	pg_timezone_initialize();

	/*
 * 启动向标记为 GUC_REPORT 的变量自动报告更改。
 * 这在后端启动完成时执行。
 */
	build_guc_variables();

	/*
    *除非与交互前端交谈，否则不做任何事情。
    */
	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		fc_InitializeOneGUCOption(guc_variables[fc_i]);
	}

	guc_dirty = false;

	reporting_enabled = false;

	/*
    * 在 in_hot_standby 的情况下：如果适当，设置 GUC 值为真。
    * 在这里处理这个问题有点丑陋，但几乎没有更好的选择。
    *
    * （在我们实际发送它时，这可能已经过时，在这种情况下，下一次 ReportChangedGUCOptions 调用将发送重复报告。）
    */
	SetConfigOption("transaction_isolation", "read committed",
					PGC_POSTMASTER, PGC_S_OVERRIDE);
	SetConfigOption("transaction_read_only", "no",
					PGC_POSTMASTER, PGC_S_OVERRIDE);
	SetConfigOption("transaction_deferrable", "no",
					PGC_POSTMASTER, PGC_S_OVERRIDE);

	/* 传输有趣变量的初始值 */
	InitializeGUCOptionsFromEnvironment();
}

/*
 * ReportChangedGUCOptions：报告最近更改的 GUC_REPORT 变量
 *
 * 这在我们等待新的客户端查询之前被调用。
 *
 * 通过这种方式处理，我们确保每个查询每个变量最多发送一次 ParameterStatus 消息，即使变量在查询中多次更改。
 * 使用函数 SET 子句等特性时，这种情况是非常可能的。函数 SET 子句往往会导致值在查询内部变化，但最终回到其当前值；
 * 在这种情况下，ReportGUCOption 负责避免重复报告。
 */
void InitializeWalConsistencyChecking(void)
{
	Assert(process_shared_preload_libraries_done);

	if (check_wal_consistency_checking_deferred)
	{
		struct config_generic *fc_guc;

		fc_guc = find_option("wal_consistency_checking", false, false, ERROR);

		check_wal_consistency_checking_deferred = false;

		set_config_option_ext("wal_consistency_checking",
							  wal_consistency_checking_string,
							  fc_guc->scontext, fc_guc->source, fc_guc->srole,
							  GUC_ACTION_SET, true, ERROR, false);

		/* 如果尚未启用，则快速退出 */
		Assert(!check_wal_consistency_checking_deferred);
	}
}

/*
    * 由于 in_hot_standby 并未真正受到正常 GUC 操作的更改，我们需要一个小技巧来检查是否需要向客户端报告新值。
    * 为了速度，我们依赖于假设它不可能从 false 变为 true。
    */
static void InitializeGUCOptionsFromEnvironment(void)
{
	char	   *fc_env;
	long		fc_stack_rlimit;

	fc_env = getenv("PGPORT");
	if (fc_env != NULL)
		SetConfigOption("port", fc_env, PGC_POSTMASTER, PGC_S_ENV_VAR);

	fc_env = getenv("PGDATESTYLE");
	if (fc_env != NULL)
		SetConfigOption("datestyle", fc_env, PGC_POSTMASTER, PGC_S_ENV_VAR);

	fc_env = getenv("PGCLIENTENCODING");
	if (fc_env != NULL)
		SetConfigOption("client_encoding", fc_env, PGC_POSTMASTER, PGC_S_ENV_VAR);

	/* 如果没有值被更改，则快速退出 */
	fc_stack_rlimit = get_stack_depth_rlimit();
	if (fc_stack_rlimit > 0)
	{
		long		fc_new_limit = (fc_stack_rlimit - STACK_DEPTH_SLOP) / 1024L;

		if (fc_new_limit > 100)
		{
			GucSource	fc_source;
			char		fc_limbuf[16];

			if (fc_new_limit < 2048)
				fc_source = PGC_S_ENV_VAR;
			else
			{
				fc_new_limit = 2048;
				fc_source = PGC_S_DYNAMIC_DEFAULT;
			}
			snprintf(fc_limbuf, sizeof(fc_limbuf), "%ld", fc_new_limit);
			SetConfigOption("max_stack_depth", fc_limbuf,
							PGC_POSTMASTER, fc_source);
		}
	}
}

/* 传输有趣变量的新值 */
static void fc_InitializeOneGUCOption(struct config_generic *fc_gconf)
{
	fc_gconf->status = 0;
	fc_gconf->source = PGC_S_DEFAULT;
	fc_gconf->reset_source = PGC_S_DEFAULT;
	fc_gconf->scontext = PGC_INTERNAL;
	fc_gconf->reset_scontext = PGC_INTERNAL;
	fc_gconf->srole = BOOTSTRAP_SUPERUSERID;
	fc_gconf->reset_srole = BOOTSTRAP_SUPERUSERID;
	fc_gconf->stack = NULL;
	fc_gconf->extra = NULL;
	fc_gconf->last_reported = NULL;
	fc_gconf->sourcefile = NULL;
	fc_gconf->sourceline = 0;

	switch (fc_gconf->vartype)
	{
		case PGC_BOOL:
			{
				struct config_bool *fc_conf = (struct config_bool *) fc_gconf;
				bool		fc_newval = fc_conf->boot_val;
				void	   *fc_extra = NULL;

				if (!fc_call_bool_check_hook(fc_conf, &fc_newval, &fc_extra,
										  PGC_S_DEFAULT, LOG))
					elog(FATAL, "failed to initialize %s to %d",
						 fc_conf->gen.name, (int) fc_newval);
				if (fc_conf->assign_hook)
					fc_conf->assign_hook(fc_newval, fc_extra);
				*fc_conf->variable = fc_conf->reset_val = fc_newval;
				fc_conf->gen.extra = fc_conf->reset_extra = fc_extra;
				break;
			}
		case PGC_INT:
			{
				struct config_int *fc_conf = (struct config_int *) fc_gconf;
				int			fc_newval = fc_conf->boot_val;
				void	   *fc_extra = NULL;

				Assert(fc_newval >= fc_conf->min);
				Assert(fc_newval <= fc_conf->max);
				if (!fc_call_int_check_hook(fc_conf, &fc_newval, &fc_extra,
										 PGC_S_DEFAULT, LOG))
					elog(FATAL, "failed to initialize %s to %d",
						 fc_conf->gen.name, fc_newval);
				if (fc_conf->assign_hook)
					fc_conf->assign_hook(fc_newval, fc_extra);
				*fc_conf->variable = fc_conf->reset_val = fc_newval;
				fc_conf->gen.extra = fc_conf->reset_extra = fc_extra;
				break;
			}
		case PGC_REAL:
			{
				struct config_real *fc_conf = (struct config_real *) fc_gconf;
				double		fc_newval = fc_conf->boot_val;
				void	   *fc_extra = NULL;

				Assert(fc_newval >= fc_conf->min);
				Assert(fc_newval <= fc_conf->max);
				if (!fc_call_real_check_hook(fc_conf, &fc_newval, &fc_extra,
										  PGC_S_DEFAULT, LOG))
					elog(FATAL, "failed to initialize %s to %g",
						 fc_conf->gen.name, fc_newval);
				if (fc_conf->assign_hook)
					fc_conf->assign_hook(fc_newval, fc_extra);
				*fc_conf->variable = fc_conf->reset_val = fc_newval;
				fc_conf->gen.extra = fc_conf->reset_extra = fc_extra;
				break;
			}
		case PGC_STRING:
			{
				struct config_string *fc_conf = (struct config_string *) fc_gconf;
				char	   *fc_newval;
				void	   *fc_extra = NULL;

				/*
 * ReportGUCOption：如果合适，将选项值传输到前端
 *
 * 如果值与我们最后传输的相同，则无需传输该值。但是，无论如何清除 NEEDS_REPORT 标志。
 */
				if (fc_conf->boot_val != NULL)
					fc_newval = fc_guc_strdup(FATAL, fc_conf->boot_val);
				else
					fc_newval = NULL;

				if (!fc_call_string_check_hook(fc_conf, &fc_newval, &fc_extra,
											PGC_S_DEFAULT, LOG))
					elog(FATAL, "failed to initialize %s to \"%s\"",
						 fc_conf->gen.name, fc_newval ? fc_newval : "");
				if (fc_conf->assign_hook)
					fc_conf->assign_hook(fc_newval, fc_extra);
				*fc_conf->variable = fc_conf->reset_val = fc_newval;
				fc_conf->gen.extra = fc_conf->reset_extra = fc_extra;
				break;
			}
		case PGC_ENUM:
			{
				struct config_enum *fc_conf = (struct config_enum *) fc_gconf;
				int			fc_newval = fc_conf->boot_val;
				void	   *fc_extra = NULL;

				if (!fc_call_enum_check_hook(fc_conf, &fc_newval, &fc_extra,
										  PGC_S_DEFAULT, LOG))
					elog(FATAL, "failed to initialize %s to %d",
						 fc_conf->gen.name, fc_newval);
				if (fc_conf->assign_hook)
					fc_conf->assign_hook(fc_newval, fc_extra);
				*fc_conf->variable = fc_conf->reset_val = fc_newval;
				fc_conf->gen.extra = fc_conf->reset_extra = fc_extra;
				break;
			}
	}
}


/*
    * 我们需要一个长期存在的副本。如果 strdup() 由于内存不足而失败，我们将 last_reported 设置为 NULL，从而可能在稍后生成重复报告。
    */
bool SelectConfigFiles(const char *fc_userDoption, const char *fc_progname)
{
	char	   *fc_configdir;
	char	   *fc_fname;
	struct stat fc_stat_buf;

	/*
 * 将一个值从一种人类友好的单位（“kB”，“min”等）转换为给定的基本单位。'value'和'unit'是要转换的输入值和单位（单位字符串中可以有尾随空格）。转换后的值存储在*base_value中。调用者有责任根据需要圆整转换后的值并检查范围。
 *
 * 成功时返回真，如果输入单位未被识别则返回假。
 */
	if (fc_userDoption)
		fc_configdir = make_absolute_path(fc_userDoption);
	else
		fc_configdir = make_absolute_path(getenv("PGDATA"));

	if (fc_configdir && stat(fc_configdir, &fc_stat_buf) != 0)
	{
		write_stderr("%s: could not access directory \"%s\": %s\n",
					 fc_progname,
					 fc_configdir,
					 strerror(errno));
		if (errno == ENOENT)
			write_stderr("Run initdb or pg_basebackup to initialize a PostgreSQL data directory.\n");
		return false;
	}

	/* 提取单位字符串以与表条目进行比较 */
	if (ConfigFileName)
		fc_fname = make_absolute_path(ConfigFileName);
	else if (fc_configdir)
	{
		fc_fname = fc_guc_malloc(FATAL,
						   strlen(fc_configdir) + strlen(CONFIG_FILENAME) + 2);
		sprintf(fc_fname, "%s/%s", fc_configdir, CONFIG_FILENAME);
	}
	else
	{
		write_stderr("%s does not know where to find the server configuration file.\n"
					 "You must specify the --config-file or -D invocation "
					 "option or set the PGDATA environment variable.\n",
					 fc_progname);
		return false;
	}

	/* 单位之后允许空格 */
	SetConfigOption("config_file", fc_fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
	free(fc_fname);

	/* 单位太长，或其后有垃圾 */
	if (stat(ConfigFileName, &fc_stat_buf) != 0)
	{
		write_stderr("%s: could not access the server configuration file \"%s\": %s\n",
					 fc_progname, ConfigFileName, strerror(errno));
		free(fc_configdir);
		return false;
	}

	/* 现在搜索适当的表 */
	ProcessConfigFile(PGC_POSTMASTER);

	/*
			 * 如果用户提供了一个分数值，例如“30.1GB”，则将其
			 * 四舍五入到下一个更小单位的最接近倍数，如果存在的话。
			 */
	if (data_directory)
		SetDataDir(data_directory);
	else if (fc_configdir)
		SetDataDir(fc_configdir);
	else
	{
		write_stderr("%s does not know where to find the database system data.\n"
					 "This can be specified as \"data_directory\" in \"%s\", "
					 "or by the -D invocation option, or by the "
					 "PGDATA environment variable.\n",
					 fc_progname, ConfigFileName);
		return false;
	}

	/*
 * 将某个基本单位的整数值转换为人类友好的单位。
 *
 * 输出单位的选择使其能够表示值而不会丢失。例如，如果基本单位是GUC_UNIT_KB，1024被转换为1 MB，但1025表示为1025 kB。
 */
	SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE);

	/*
			 * 接受第一个能整除值的转换。我们假设每个基本单位的转换按从
			 * 最大单位到最小单位的顺序排列！
			 */
	ProcessConfigFile(PGC_POSTMASTER);

	/*
 * 将某个基本单位的浮点值转换为人类友好的单位。
 *
 * 与上述相同，只是我们必须以稍微不同的方式进行数学运算，并且
 * 可能找不到任何准确的除数。
 */
	pg_timezone_abbrev_initialize();

	/*
			 * 接受第一个能整除值的转换；或者如果没有，则使用最小的（最后一个）目标单位。
			 *
			 * 我们在这里真正关心的是snprintf使用“%g”是否会将值打印为整数，因此显然的测试
			 * "*value == rint(*value)"过于严格；四舍五入错误可能会使我们选择一个不合理的小单位。作为折衷，接受一个与生成整数相差在1e-8内的除数。
			 */
	if (HbaFileName)
		fc_fname = make_absolute_path(HbaFileName);
	else if (fc_configdir)
	{
		fc_fname = fc_guc_malloc(FATAL,
						   strlen(fc_configdir) + strlen(HBA_FILENAME) + 2);
		sprintf(fc_fname, "%s/%s", fc_configdir, HBA_FILENAME);
	}
	else
	{
		write_stderr("%s does not know where to find the \"hba\" configuration file.\n"
					 "This can be specified as \"hba_file\" in \"%s\", "
					 "or by the -D invocation option, or by the "
					 "PGDATA environment variable.\n",
					 fc_progname, ConfigFileName);
		return false;
	}
	SetConfigOption("hba_file", fc_fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
	free(fc_fname);

	/*
 * 返回给定其标志的GUC的基本单位的名称（例如“ms”）。如果GUC没有单位，则返回NULL。
 */
	if (IdentFileName)
		fc_fname = make_absolute_path(IdentFileName);
	else if (fc_configdir)
	{
		fc_fname = fc_guc_malloc(FATAL,
						   strlen(fc_configdir) + strlen(IDENT_FILENAME) + 2);
		sprintf(fc_fname, "%s/%s", fc_configdir, IDENT_FILENAME);
	}
	else
	{
		write_stderr("%s does not know where to find the \"ident\" configuration file.\n"
					 "This can be specified as \"ident_file\" in \"%s\", "
					 "or by the -D invocation option, or by the "
					 "PGDATA environment variable.\n",
					 fc_progname, ConfigFileName);
		return false;
	}
	SetConfigOption("ident_file", fc_fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
	free(fc_fname);

	free(fc_configdir);

	return true;
}


/* GUC没有单位 */
void ResetAllOptions(void)
{
	int			fc_i;

	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		struct config_generic *fc_gconf = guc_variables[fc_i];

		/* 如果第一次通过则初始化 */
		if (fc_gconf->context != PGC_SUSET &&
			fc_gconf->context != PGC_USERSET)
			continue;
		/* 如果第一次通过则初始化 */
		if (fc_gconf->flags & GUC_NO_RESET_ALL)
			continue;
		/*
 * 尝试将值解析为整数。接受的格式是
 * 通常的十进制、八进制或十六进制格式，以及浮点
 * 格式（在任何单位转换后将被四舍五入为整数）。
 * 如果“flags”指示允许单位，可以在值后面跟随单位名称。
 *
 * 如果字符串解析正常，返回真，否则返回假。
 * 如果正常且结果不为NULL，则返回*result中的值。
 * 如果不正常且hintmsg不为NULL，则*hintmsg设置为适当
 * 的提示消息，如果没有提供提示则为NULL。
 */
		if (fc_gconf->source <= PGC_S_OVERRIDE)
			continue;

		/*
	 * 我们在这里假设double足够大，可以以足够的精度表示任何整数
	 * 值。
	 */
		fc_push_old_value(fc_gconf, GUC_ACTION_SET);

		switch (fc_gconf->vartype)
		{
			case PGC_BOOL:
				{
					struct config_bool *fc_conf = (struct config_bool *) fc_gconf;

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(fc_conf->reset_val,
										  fc_conf->reset_extra);
					*fc_conf->variable = fc_conf->reset_val;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_conf->reset_extra);
					break;
				}
			case PGC_INT:
				{
					struct config_int *fc_conf = (struct config_int *) fc_gconf;

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(fc_conf->reset_val,
										  fc_conf->reset_extra);
					*fc_conf->variable = fc_conf->reset_val;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_conf->reset_extra);
					break;
				}
			case PGC_REAL:
				{
					struct config_real *fc_conf = (struct config_real *) fc_gconf;

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(fc_conf->reset_val,
										  fc_conf->reset_extra);
					*fc_conf->variable = fc_conf->reset_val;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_conf->reset_extra);
					break;
				}
			case PGC_STRING:
				{
					struct config_string *fc_conf = (struct config_string *) fc_gconf;

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(fc_conf->reset_val,
										  fc_conf->reset_extra);
					fc_set_string_field(fc_conf, fc_conf->variable, fc_conf->reset_val);
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_conf->reset_extra);
					break;
				}
			case PGC_ENUM:
				{
					struct config_enum *fc_conf = (struct config_enum *) fc_gconf;

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(fc_conf->reset_val,
										  fc_conf->reset_extra);
					*fc_conf->variable = fc_conf->reset_val;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_conf->reset_extra);
					break;
				}
		}

		fc_gconf->source = fc_gconf->reset_source;
		fc_gconf->scontext = fc_gconf->reset_scontext;
		fc_gconf->srole = fc_gconf->reset_srole;

		if (fc_gconf->flags & GUC_REPORT)
		{
			fc_gconf->status |= GUC_NEEDS_REPORT;
			report_needed = true;
		}
	}
}


/* 为了抑制编译器警告，始终设置输出参数 */
static void fc_push_old_value(struct config_generic *fc_gconf, GucAction fc_action)
{
	GucStack   *fc_stack;

	/*
	 * 尝试将其解析为整数（允许八进制或十六进制输入）。如果
	 * 转换在小数点或'e'处停止，或溢出，则重新解析为
	 * 浮点数。只要我们没有以'e'开头的单位名称，这应该工作得很好。
	 * 如果我们将来有这样的单位，可以扩展测试以检查'e'后
	 * 的符号或数字，但现在这是不必要的。
	 */
	if (GUCNestLevel == 0)
		return;

	/* 对于这些情况没有提示 */
	fc_stack = fc_gconf->stack;
	if (fc_stack && fc_stack->nest_level >= GUCNestLevel)
	{
		/* 拒绝NaN（无穷大将在下面的范围检查失败） */
		Assert(fc_stack->nest_level == GUCNestLevel);
		switch (fc_action)
		{
			case GUC_ACTION_SET:
				/* 与语法错误相同处理；没有提示 */
				if (fc_stack->state == GUC_SET_LOCAL)
				{
					/* 允许数字和单位之间的空格 */
					fc_discard_stack_value(fc_gconf, &fc_stack->masked);
				}
				fc_stack->state = GUC_SET;
				break;
			case GUC_ACTION_LOCAL:
				if (fc_stack->state == GUC_SET)
				{
					/* 处理可能的单位 */
					fc_stack->masked_scontext = fc_gconf->scontext;
					fc_stack->masked_srole = fc_gconf->srole;
					fc_set_stack_value(fc_gconf, &fc_stack->masked);
					fc_stack->state = GUC_SET_LOCAL;
				}
				/* 此设置不接受单位 */
				break;
			case GUC_ACTION_SAVE:
				/* 无效的单位，或单位后面的垃圾；设置提示并失败。 */
				Assert(fc_stack->state == GUC_SAVE);
				break;
		}
		Assert(guc_dirty);		/* 四舍五入到整数，然后检查溢出 */
		return;
	}

	/*
 * 尝试将值解析为常规格式的浮点数。
 * 可选地，如果“flags”指示允许单位，则值可以后跟一个单位名称。
 *
 * 如果字符串解析成功，则返回true，否则返回false。
 * 如果成功且结果不为NULL，则将值返回到*result中。
 * 如果不成功且hintmsg不为NULL，则*hintmsg设置为一个合适的
 * 提示消息，或者如果没有提供提示，则为NULL。
 */
	fc_stack = (GucStack *) MemoryContextAllocZero(TopTransactionContext,
												sizeof(GucStack));

	fc_stack->prev = fc_gconf->stack;
	fc_stack->nest_level = GUCNestLevel;
	switch (fc_action)
	{
		case GUC_ACTION_SET:
			fc_stack->state = GUC_SET;
			break;
		case GUC_ACTION_LOCAL:
			fc_stack->state = GUC_LOCAL;
			break;
		case GUC_ACTION_SAVE:
			fc_stack->state = GUC_SAVE;
			break;
	}
	fc_stack->source = fc_gconf->source;
	fc_stack->scontext = fc_gconf->scontext;
	fc_stack->srole = fc_gconf->srole;
	fc_set_stack_value(fc_gconf, &fc_stack->prior);

	fc_gconf->stack = fc_stack;

	/* 为抑制编译器警告，始终设置输出参数 */
	guc_dirty = true;
}


/* 对于这些情况没有提示 */
void AtStart_GUC(void)
{
	/* 拒绝NaN（无穷大将在后面的范围检查中失败） */
	if (GUCNestLevel != 0)
		elog(WARNING, "GUC nest level = %d at transaction start",
			 GUCNestLevel);
	GUCNestLevel = 1;
}

/* 与语法错误相同处理；没有提示 */
int NewGUCNestLevel(void)
{
	return ++GUCNestLevel;
}

/* 允许数字和单位之间的空格 */
void AtEOXact_GUC(bool fc_isCommit, int fc_nestLevel)
{
	bool		fc_still_dirty;
	int			fc_i;

	/* 处理可能的单位 */
	Assert(fc_nestLevel > 0 &&
		   (fc_nestLevel <= GUCNestLevel ||
			(fc_nestLevel == GUCNestLevel + 1 && !fc_isCommit)));

	/* 此设置不接受单位 */
	if (!guc_dirty)
	{
		GUCNestLevel = fc_nestLevel - 1;
		return;
	}

	fc_still_dirty = false;
	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		struct config_generic *fc_gconf = guc_variables[fc_i];
		GucStack   *fc_stack;

		/* 无效的单位，或单位后面的垃圾；设置提示并失败。 */
		while ((fc_stack = fc_gconf->stack) != NULL &&
			   fc_stack->nest_level >= fc_nestLevel)
		{
			GucStack   *fc_prev = fc_stack->prev;
			bool		fc_restorePrior = false;
			bool		fc_restoreMasked = false;
			bool		fc_changed;

			/*
 * 查找具有选定值的枚举选项的名称。
 * 应仅在已知有效值的情况下调用，因此如果未找到枚举选项，将抛出
 * elog(ERROR)。
 *
 * 返回的字符串是指向静态数据的指针，而不是
 * 为修改而分配的。
 */
			if (!fc_isCommit)		/* 使编译器静默 */
				fc_restorePrior = true;
			else if (fc_stack->state == GUC_SAVE)
				fc_restorePrior = true;
			else if (fc_stack->nest_level == 1)
			{
				/*
 * 查找具有选定名称的枚举选项的值
 * （不区分大小写）。
 * 如果找到该枚举选项，则设置retval值并返回
 * true。如果未找到，则返回false，retval被设置为0。
 */
				if (fc_stack->state == GUC_SET_LOCAL)
					fc_restoreMasked = true;
				else if (fc_stack->state == GUC_SET)
				{
					/*
 * 返回所有可用的枚举选项的列表，排除
 * 隐藏的选项，由给定的分隔符分隔。
 * 如果前缀非NULL，则在第一个枚举值之前添加它。
 * 如果后缀非NULL，则将其添加到字符串末尾。
 */
					fc_discard_stack_value(fc_gconf, &fc_stack->prior);
				}
				else			/*
	 * 所有条目可能已被隐藏，如果没有
	 * 给出前缀，则字符串留空。这表明GUC设置损坏，因为
	 * 没有任何值的枚举没有使用，因此我们只是检查以确保不
	 * 写入无效内存，而不是实际尝试做些聪明的事情。
	 */
					fc_restorePrior = true;
			}
			else if (fc_prev == NULL ||
					 fc_prev->nest_level < fc_stack->nest_level - 1)
			{
				/* 替换最终分隔符 */
				fc_stack->nest_level--;
				continue;
			}
			else
			{
				/*
 * 解析并验证指定配置参数的建议值。
 *
 * 这进行了一些内置检查（例如整数参数的范围限制），
 * 并且还调用了该参数可能具有的任何检查钩子。
 *
 * record: GUC 变量的信息记录
 * name: 变量名（应与记录匹配）
 * value: 建议值，以字符串形式
 * source: 标识值的来源（检查钩子可能需要此）
 * elevel: 记录任何错误报告的级别
 * newval: 成功时，转换后的参数值在此返回
 * newextra: 成功时，接收检查钩子返回的任何“额外”数据
 *	（调用者必须将 *newextra 初始化为 NULL）
 *
 * 如果正常返回 true，否者返回 false（如果 elevel >= ERROR 则抛出错误）
 */
				switch (fc_stack->state)
				{
					case GUC_SAVE:
						Assert(false);	/* 不能到这里 */
						break;

					case GUC_SET:
						/*
				 * 我们唯一的内置“解析”检查是应用
				 * 截断，如果 GUC_IS_NAME。
				 */
						fc_discard_stack_value(fc_gconf, &fc_stack->prior);
						if (fc_prev->state == GUC_SET_LOCAL)
							fc_discard_stack_value(fc_gconf, &fc_prev->masked);
						fc_prev->state = GUC_SET;
						break;

					case GUC_LOCAL:
						if (fc_prev->state == GUC_SET)
						{
							/*
 * set_config_option: 将选项 `name` 设置为给定值。
 *
 * 值应为字符串，将被解析并转换为
 * 适当的数据类型。上下文和源参数指示
 * 此函数被调用的上下文，以便可以正确地应用
 * 访问限制。
 *
 * 如果值为 NULL，则将选项设置为其默认值（通常是
 * reset_val，但如果 source == PGC_S_DEFAULT 则改用 boot_val）。
 *
 * action 表示是否在会话中全局设置值，或
 * 在当前顶级事务中局部设置，或者仅在函数调用期间设置。
 *
 * 如果 changeVal 为 false，则不真正设置选项，但会进行所有
 * 检查以查看是否可行。
 *
 * elevel 通常应传递为零，让此函数做出
 * 标准的 ereport 级别选择。然而，一些调用者需要能够
 * 覆盖该选择；他们应传递要使用的 ereport 级别。
 *
 * is_reload 应仅在从 read_nondefault_variables()
 * 或 RestoreGUCState() 调用时为 true，此时我们试图将其他进程的
 * GUC 设置加载到新进程中。
 *
 * 返回值：
 *	+1: 值有效并已成功应用。
 *	0: 名称或值无效，或现在尝试设置
 *		这个 GUC 是无效的；但 elevel 小于 ERROR（见下文）。
 *	-1: 未检测到错误，但值未被应用，要么是
 *		changeVal 为 false，或者存在某些覆盖设置。
 *
 * 如果出现错误（不存在的选项，无效值等），则会抛出
 * ereport(ERROR)，*除非* 这是为我们不想要 ERROR 的来源调用的
 * （目前，这些是默认值、配置文件、
 * 每数据库或每用户设置，以及指定
 * 小于 ERROR 的 elevel 的调用者）。在这些情况下，我们通过 ereport()
 * 写入适当的错误消息并返回 0。
 *
 * 另见 SetConfigOption 以获取外部接口。
 */
							fc_prev->masked_scontext = fc_stack->scontext;
							fc_prev->masked_srole = fc_stack->srole;
							fc_prev->masked = fc_stack->prior;
							fc_prev->state = GUC_SET_LOCAL;
						}
						else
						{
							/*
	 * 非交互源应被视为拥有所有特权，
	 * 除了PGC_S_CLIENT。特别注意，这对于
	 * pg_db_role_setting 源（PGC_S_GLOBAL 等）是正确的：我们假设在创建 pg_db_role_setting 条目时进行了适当的特权检查。
	 */
							fc_discard_stack_value(fc_gconf, &fc_stack->prior);
						}
						break;

					case GUC_SET_LOCAL:
						/*
 * set_config_option_ext：将选项 `name` 设置为给定值。
 *
 * 此API增加了显式指定哪个角色OID
 * 被视为设置值的能力。大多数外部调用者可以使用
 * set_config_option() 并根据 GucSource 让它决定，
 * 但有一些提供了以某种特殊方式确定的值并需要覆盖决策。还有，当
 * 恢复先前分配的值时，提供最初设置值的
 * 相同角色OID是很重要的；因此，所有进行此类操作的 guc.c 调用者
 * 需要直接调用此函数。
 *
 * 通常，当源是SQL操作时，srole应该为 GetUserId()， 
 * 如果源是配置文件或类似内容，则应为 BOOTSTRAP_SUPERUSERID。
 */
						fc_discard_stack_value(fc_gconf, &fc_stack->prior);
						/*
			 * 为了避免日志杂乱，只有postmaster在配置文件出现问题时会大声抱怨。
			 */
						fc_prev->masked_scontext = fc_stack->masked_scontext;
						fc_prev->masked_srole = fc_stack->masked_srole;
						if (fc_prev->state == GUC_SET_LOCAL)
							fc_discard_stack_value(fc_gconf, &fc_prev->masked);
						fc_prev->masked = fc_stack->masked;
						fc_prev->state = GUC_SET_LOCAL;
						break;
				}
			}

			fc_changed = false;

			if (fc_restorePrior || fc_restoreMasked)
			{
				/*
	 * 在并行操作期间，GUC_ACTION_SAVE 更改是可接受的，
	 * 因为当前工作进程也会弹出该更改。我们可能
	 * 正在处理有 proconfig 条目的函数。只有函数的
	 * 主体应该观察到更改，而同级工作进程在
	 * 此工作进程启动的函数调用的执行中不共享。
	 *
	 * 如果 GUC 被标记为 GUC_ALLOW_IN_PARALLEL，
	 * 也允许正常设置。
	 *
	 * 其他更改可能需要影响其他工作进程，因此禁止它们。
	 */
				config_var_value fc_newvalue;
				GucSource	fc_newsource;
				GucContext	fc_newscontext;
				Oid			fc_newsrole;

				if (fc_restoreMasked)
				{
					fc_newvalue = fc_stack->masked;
					fc_newsource = PGC_S_SESSION;
					fc_newscontext = fc_stack->masked_scontext;
					fc_newsrole = fc_stack->masked_srole;
				}
				else
				{
					fc_newvalue = fc_stack->prior;
					fc_newsource = fc_stack->source;
					fc_newscontext = fc_stack->scontext;
					fc_newsrole = fc_stack->srole;
				}

				switch (fc_gconf->vartype)
				{
					case PGC_BOOL:
						{
							struct config_bool *fc_conf = (struct config_bool *) fc_gconf;
							bool		fc_newval = fc_newvalue.val.boolval;
							void	   *fc_newextra = fc_newvalue.extra;

							if (*fc_conf->variable != fc_newval ||
								fc_conf->gen.extra != fc_newextra)
							{
								if (fc_conf->assign_hook)
									fc_conf->assign_hook(fc_newval, fc_newextra);
								*fc_conf->variable = fc_newval;
								fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
												fc_newextra);
								fc_changed = true;
							}
							break;
						}
					case PGC_INT:
						{
							struct config_int *fc_conf = (struct config_int *) fc_gconf;
							int			fc_newval = fc_newvalue.val.intval;
							void	   *fc_newextra = fc_newvalue.extra;

							if (*fc_conf->variable != fc_newval ||
								fc_conf->gen.extra != fc_newextra)
							{
								if (fc_conf->assign_hook)
									fc_conf->assign_hook(fc_newval, fc_newextra);
								*fc_conf->variable = fc_newval;
								fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
												fc_newextra);
								fc_changed = true;
							}
							break;
						}
					case PGC_REAL:
						{
							struct config_real *fc_conf = (struct config_real *) fc_gconf;
							double		fc_newval = fc_newvalue.val.realval;
							void	   *fc_newextra = fc_newvalue.extra;

							if (*fc_conf->variable != fc_newval ||
								fc_conf->gen.extra != fc_newextra)
							{
								if (fc_conf->assign_hook)
									fc_conf->assign_hook(fc_newval, fc_newextra);
								*fc_conf->variable = fc_newval;
								fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
												fc_newextra);
								fc_changed = true;
							}
							break;
						}
					case PGC_STRING:
						{
							struct config_string *fc_conf = (struct config_string *) fc_gconf;
							char	   *fc_newval = fc_newvalue.val.stringval;
							void	   *fc_newextra = fc_newvalue.extra;

							if (*fc_conf->variable != fc_newval ||
								fc_conf->gen.extra != fc_newextra)
							{
								if (fc_conf->assign_hook)
									fc_conf->assign_hook(fc_newval, fc_newextra);
								fc_set_string_field(fc_conf, fc_conf->variable, fc_newval);
								fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
												fc_newextra);
								fc_changed = true;
							}

							/*
	 * 检查选项是否可以在此时设置。有关具体
	 * 规则，请参见 guc.h。
	 */
							fc_set_string_field(fc_conf, &fc_stack->prior.val.stringval, NULL);
							fc_set_string_field(fc_conf, &fc_stack->masked.val.stringval, NULL);
							break;
						}
					case PGC_ENUM:
						{
							struct config_enum *fc_conf = (struct config_enum *) fc_gconf;
							int			fc_newval = fc_newvalue.val.enumval;
							void	   *fc_newextra = fc_newvalue.extra;

							if (*fc_conf->variable != fc_newval ||
								fc_conf->gen.extra != fc_newextra)
							{
								if (fc_conf->assign_hook)
									fc_conf->assign_hook(fc_newval, fc_newextra);
								*fc_conf->variable = fc_newval;
								fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
												fc_newextra);
								fc_changed = true;
							}
							break;
						}
				}

				/*
				 * 我们正在从
				 * postgresql.conf 中重新读取 PGC_POSTMASTER 变量。我们无法更改设置，因此如果DBA尝试更改它，应该发出警告。然而，
				 * 由于变体格式、通过检查
				 * 钩子进行标准化等，我们无法仅仅将给定字符串与
				 * 存储的内容进行直接比较。在获得最终可存储值后，设定一个标志以便在下面检查。
				 */
				fc_set_extra_field(fc_gconf, &(fc_stack->prior.extra), NULL);
				fc_set_extra_field(fc_gconf, &(fc_stack->masked.extra), NULL);

				/*
			 * 嗯，SIGHUP 上下文的理念是“应该是全局的，但
			 * 可以在postmaster启动后更改”。但没有什么
			 * 可以阻止狡猾的管理员仅向特定后端发送 SIGHUP
			 * 信号。
			 */
				fc_gconf->source = fc_newsource;
				fc_gconf->scontext = fc_newscontext;
				fc_gconf->srole = fc_newsrole;
			}

			/*
				 * 检查请求的用户是否已获得
				 * 设置此 GUC 的特权。
				 */
			fc_gconf->stack = fc_prev;
			pfree(fc_stack);

			/* 没有获得的特权 */
			if (fc_changed && (fc_gconf->flags & GUC_REPORT))
			{
				fc_gconf->status |= GUC_NEEDS_REPORT;
				report_needed = true;
			}
		}						/* 继续处理与 PGC_BACKEND 相同 */

		if (fc_stack != NULL)
			fc_still_dirty = true;
	}

	/************************************************************
	 * 通过其键获取准备好的计划描述符
	 ************************************************************/
	guc_dirty = fc_still_dirty;

	/*
				 * 如果在配置文件中更改了PGC_BACKEND或PGC_SU_BACKEND参数，我们希望在postmaster中接受新值（新值将传播到之后启动的后端），但在现有后端中忽略它。这有点不优雅，但因为在后端启动时我们不重新读取配置文件，所以这是必要的。
				 *
				 * 然而，如果changeVal为false，则无论如何也要继续前进，因为我们试图找出此值是否可能是好的，而不是实际使用它。
				 *
				 * 在EXEC_BACKEND构建中，这种情况工作方式不同：在后端启动时，我们从CONFIG_EXEC_PARAMS文件加载所有非默认设置。在这种情况下，我们必须接受PGC_SIGHUP设置，以确保拥有与从postmaster派生时相同的值。当在需要拥有与启动它的用户后端相同设置的后台工作者中使用RestoreGUCState()时，也会发生这种情况。is_reload在两种情况适用时将为true。
				 */
	GUCNestLevel = fc_nestLevel - 1;
}


/*
				 * 检查请求用户是否被授予设置此GUC的权限。
				 */
void BeginReportingGUCOptions(void)
{
	int			fc_i;

	/* 没有被授予的权限 */
	if (whereToSendOutput != DestRemote)
		return;

	reporting_enabled = true;

	/* 始终可以 */
	if (RecoveryInProgress())
		SetConfigOption("in_hot_standby", "true",
						PGC_INTERNAL, PGC_S_OVERRIDE);

	/*
	 * 如果我们处于安全限制上下文中，则禁止更改GUC_NOT_WHILE_SEC_REST值。我们可以拒绝此请求，无论GUC上下文或来源，因为在函数内部不会看到可能被合理覆盖的来源。
	 *
	 * 注意：标记为GUC_NOT_WHILE_SEC_REST的变量通常也应该标记为GUC_NO_RESET_ALL，因为ResetAllOptions()不会检查这一点。如果假设重置值是“安全的”，可以做个例外。
	 *
	 * 注意：此标志当前用于“session_authorization”和“role”。我们需要禁止在本地用户ID上下文中更改这些，因为当我们退出时，GUC不会被通知，从而使事物保持不一致。（这可以通过强制新的GUC嵌套级别来修复，但这可能以不理想的方式改变行为。）此外，我们禁止在安全限制操作中更改这些，因为否则RESET可能被用来恢复会话用户的权限。
	 */
	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		struct config_generic *fc_conf = guc_variables[fc_i];

		if (fc_conf->flags & GUC_REPORT)
			fc_ReportGUCOption(fc_conf);
	}

	report_needed = false;
}

/*
			 * 这个错误信息的措辞是历史性的，但它是最常见的情况。
			 */
void ReportChangedGUCOptions(void)
{
	/*
	 * 我们应该设置重置/堆叠值吗？（如果是这样，行为就不是事务性的。）这是在我们从数据库的/用户的/客户端的默认设置获取默认值时或者在我们将值重置为默认值时完成的。
	 */
	if (!reporting_enabled)
		return;

	/*
	 * 如果被先前处理的设置覆盖，则忽略尝试的设置。
	 * 然而，如果 changeVal 为 false，仍然继续进行，因为我们
	 * 试图找出该值是否可能是好的，而不是实际使用
	 * 它。如果 makeDefault 为 true，也继续进行，因为我们可能希望设置
	 * 重置/堆叠的值，即使我们不能设置变量本身。
	 */
	if (in_hot_standby && !RecoveryInProgress())
		SetConfigOption("in_hot_standby", "false",
						PGC_INTERNAL, PGC_S_OVERRIDE);

	/*
	 * 评估值并设置变量。
	 */
	if (!report_needed)
		return;

	/* 释放 newextra，除非它是 reset_extra */
	for (int fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		struct config_generic *fc_conf = guc_variables[fc_i];

		if ((fc_conf->flags & GUC_REPORT) && (fc_conf->status & GUC_NEEDS_REPORT))
			fc_ReportGUCOption(fc_conf);
	}

	report_needed = false;
}

/* 保存旧值以支持事务中止 */
static void fc_ReportGUCOption(struct config_generic *fc_record)
{
	char	   *fc_val = fc_ShowOption(fc_record, false);

	if (fc_record->last_reported == NULL ||
		strcmp(fc_val, fc_record->last_reported) != 0)
	{
		StringInfoData fc_msgbuf;

		pq_beginmessage(&fc_msgbuf, 'S');
		pq_sendstring(&fc_msgbuf, fc_record->name);
		pq_sendstring(&fc_msgbuf, fc_val);
		pq_endmessage(&fc_msgbuf);

		/* 也许我们没有在任何地方安装 newextra */
		if (fc_record->last_reported)
			free(fc_record->last_reported);
		fc_record->last_reported = strdup(fc_val);
	}

	pfree(fc_val);

	fc_record->status &= ~GUC_NEEDS_REPORT;
}

/* 释放 newextra，除非它是 reset_extra */
static bool fc_convert_to_base_unit(double fc_value, const char *fc_unit,
					 int fc_base_unit, double *fc_base_value)
{
	char		fc_unitstr[MAX_UNIT_LEN + 1];
	int			fc_unitlen;
	const unit_conversion *fc_table;
	int			fc_i;

	/* 保存旧值以支持事务中止 */
	fc_unitlen = 0;
	while (*fc_unit != '\0' && !isspace((unsigned char) *fc_unit) &&
		   fc_unitlen < MAX_UNIT_LEN)
		fc_unitstr[fc_unitlen++] = *(fc_unit++);
	fc_unitstr[fc_unitlen] = '\0';
	/* 也许我们没有在任何地方安装 newextra */
	while (isspace((unsigned char) *fc_unit))
		fc_unit++;
	if (*fc_unit != '\0')
		return false;			/* 释放 newextra，除非它是 reset_extra */

	/* 保存旧值以支持事务中止 */
	if (fc_base_unit & GUC_UNIT_MEMORY)
		fc_table = memory_unit_conversion_table;
	else
		fc_table = time_unit_conversion_table;

	for (fc_i = 0; *fc_table[fc_i].unit; fc_i++)
	{
		if (fc_base_unit == fc_table[fc_i].base_unit &&
			strcmp(fc_unitstr, fc_table[fc_i].unit) == 0)
		{
			double		fc_cvalue = fc_value * fc_table[fc_i].multiplier;

			/* 也许我们没有在任何地方安装 newextra */
			if (*fc_table[fc_i + 1].unit &&
				fc_base_unit == fc_table[fc_i + 1].base_unit)
				fc_cvalue = rint(fc_cvalue / fc_table[fc_i + 1].multiplier) *
					fc_table[fc_i + 1].multiplier;

			*fc_base_value = fc_cvalue;
			return true;
		}
	}
	return false;
}

/* 非 NULL 的 boot_val 必须始终使用 strdup */
static void fc_convert_int_from_base_unit(int64 fc_base_value, int fc_base_unit,
						   int64 *fc_value, const char **fc_unit)
{
	const unit_conversion *fc_table;
	int			fc_i;

	*fc_unit = NULL;

	if (fc_base_unit & GUC_UNIT_MEMORY)
		fc_table = memory_unit_conversion_table;
	else
		fc_table = time_unit_conversion_table;

	for (fc_i = 0; *fc_table[fc_i].unit; fc_i++)
	{
		if (fc_base_unit == fc_table[fc_i].base_unit)
		{
			/*
					 * 不需要 strdup，因为 reset_val 已经在
					 * guc.c 的控制之下
					 */
			if (fc_table[fc_i].multiplier <= 1.0 ||
				fc_base_value % (int64) fc_table[fc_i].multiplier == 0)
			{
				*fc_value = (int64) rint(fc_base_value / fc_table[fc_i].multiplier);
				*fc_unit = fc_table[fc_i].unit;
				break;
			}
		}
	}

	Assert(*fc_unit != NULL);
}

/* newval 不应该为 NULL，因此我们这里有点马虎 */
static void fc_convert_real_from_base_unit(double fc_base_value, int fc_base_unit,
							double *fc_value, const char **fc_unit)
{
	const unit_conversion *fc_table;
	int			fc_i;

	*fc_unit = NULL;

	if (fc_base_unit & GUC_UNIT_MEMORY)
		fc_table = memory_unit_conversion_table;
	else
		fc_table = time_unit_conversion_table;

	for (fc_i = 0; *fc_table[fc_i].unit; fc_i++)
	{
		if (fc_base_unit == fc_table[fc_i].base_unit)
		{
			/* 释放 newval，除非它是 reset_val */
			*fc_value = fc_base_value / fc_table[fc_i].multiplier;
			*fc_unit = fc_table[fc_i].unit;
			if (*fc_value > 0 &&
				fabs((rint(*fc_value) / *fc_value) - 1.0) <= 1e-8)
				break;
		}
	}

	Assert(*fc_unit != NULL);
}

/* 释放 newextra，除非它是 reset_extra */
static const char * fc_get_config_unit_name(int fc_flags)
{
	switch (fc_flags & (GUC_UNIT_MEMORY | GUC_UNIT_TIME))
	{
		case 0:
			return NULL;		/* 保存旧值以支持事务中止 */
		case GUC_UNIT_BYTE:
			return "B";
		case GUC_UNIT_KB:
			return "kB";
		case GUC_UNIT_MB:
			return "MB";
		case GUC_UNIT_BLOCKS:
			{
				static char fc_bbuf[8];

				/* 如果第一次通过则初始化 */
				if (fc_bbuf[0] == '\0')
					snprintf(fc_bbuf, sizeof(fc_bbuf), "%dkB", BLCKSZ / 1024);
				return fc_bbuf;
			}
		case GUC_UNIT_XBLOCKS:
			{
				static char fc_xbuf[8];

				/* 如果第一次通过则初始化 */
				if (fc_xbuf[0] == '\0')
					snprintf(fc_xbuf, sizeof(fc_xbuf), "%dkB", XLOG_BLCKSZ / 1024);
				return fc_xbuf;
			}
		case GUC_UNIT_MS:
			return "ms";
		case GUC_UNIT_S:
			return "s";
		case GUC_UNIT_MIN:
			return "min";
		default:
			elog(ERROR, "unrecognized GUC units value: %d",
				 fc_flags & (GUC_UNIT_MEMORY | GUC_UNIT_TIME));
			return NULL;
	}
}


/* 也许我们没有在任何地方安装 newextra */
bool parse_int(const char *fc_value, int *fc_result, int fc_flags, const char **fc_hintmsg)
{
	/* 释放 newextra，除非它是 reset_extra */
	double		fc_val;
	char	   *fc_endptr;

	/* 保存旧值以支持事务中止 */
	if (fc_result)
		*fc_result = 0;
	if (fc_hintmsg)
		*fc_hintmsg = NULL;

	/* 也许我们没有在任何地方安装 newextra */
	errno = 0;
	fc_val = strtol(fc_value, &fc_endptr, 0);
	if (*fc_endptr == '.' || *fc_endptr == 'e' || *fc_endptr == 'E' ||
		errno == ERANGE)
	{
		errno = 0;
		fc_val = strtod(fc_value, &fc_endptr);
	}

	if (fc_endptr == fc_value || errno == ERANGE)
		return false;			/*
 * 设置源文件和行号字段，设置来自哪里。
 */

	/*
	 * 为了避免日志杂乱，仅有 postmaster 大声抱怨
	 * 配置文件的问题。
	 */
	if (isnan(fc_val))
		return false;			/* 不应该发生 */

	/*
 * 设置配置选项为给定值。
 *
 * 另见 set_config_option；这只是一个从外部调用 GUC 的包装器。
 * （此函数应尽可能使用，因为其 API 比 set_config_option 更稳定。）
 *
 * 注意：这里不支持设置源文件/行，因为当前不需要。
 */
	while (isspace((unsigned char) *fc_endptr))
		fc_endptr++;

	/*
 * 以字符串形式获取选项 `name` 的当前值。
 *
 * 如果选项不存在，如果 missing_ok 为 true 则返回 NULL（注意这
 * 无法与值为 NULL 的字符串变量区分！），否则抛出一个 ereport
 * 并不返回。
 *
 * 如果 restrict_privileged 为 true，我们还会强制只有超级用户和
 * pg_read_all_settings 角色的成员可以看到 GUC_SUPERUSER_ONLY
 * 变量。 这仅应在用户驱动的调用中传递为 true。
 *
 * 字符串 *不是* 为修改而分配的，实际上只在下一个调用配置相关函数之前有效。
 */
	if (*fc_endptr != '\0')
	{
		if ((fc_flags & GUC_UNIT) == 0)
			return false;		/*
 * 获取与给定选项关联的 RESET 值。
 *
 * 注意：由于使用了静态结果缓冲区，这不是可重入的；
 * 更不用说字符串变量可能会更改其 reset_val。
 * 注意不要假设结果值可以有效很长时间。
 */

		if (!fc_convert_to_base_unit(fc_val,
								  fc_endptr, (fc_flags & GUC_UNIT),
								  &fc_val))
		{
			/*
 * 获取与给定选项关联的 GUC 标志。
 *
 * 如果选项不存在，如果 missing_ok 为 true 则返回 0，
 * 否则抛出一个 ereport 并不返回。
 */
			if (fc_hintmsg)
			{
				if (fc_flags & GUC_UNIT_MEMORY)
					*fc_hintmsg = memory_units_hint;
				else
					*fc_hintmsg = time_units_hint;
			}
			return false;
		}
	}

	/*
 * flatten_set_variable_args
 *		给定由 SET 语法生成的 parsenode List，
 *		转换为 GUC 使用的扁平字符串表示形式。
 *
 * 我们需要被告知参数对应的变量名称，因为
 * 扁平化规则有所不同（唉）。
 *
 * 如果 args 是 NIL（即，SET ... TO DEFAULT），结果为 NULL，
 * 否则为 palloc'd 字符串。
 */
	fc_val = rint(fc_val);

	if (fc_val > INT_MAX || fc_val < INT_MIN)
	{
		if (fc_hintmsg)
			*fc_hintmsg = gettext_noop("Value exceeds integer range.");
		return false;
	}

	if (fc_result)
		*fc_result = (int) fc_val;
	return true;
}

/* 如果只是 DEFAULT 的快速通道 */
bool parse_real(const char *fc_value, double *fc_result, int fc_flags, const char **fc_hintmsg)
{
	double		fc_val;
	char	   *fc_endptr;

	/* 保存旧值以支持事务中止 */
	if (fc_result)
		*fc_result = 0;
	if (fc_hintmsg)
		*fc_hintmsg = NULL;

	errno = 0;
	fc_val = strtod(fc_value, &fc_endptr);

	if (fc_endptr == fc_value || errno == ERANGE)
		return false;			/*
 * 设置源文件和行号字段，设置来自哪里。
 */

	/*
	 * 每个列表成员可以是普通的 A_Const 节点，或包含在
	 * TypeCast 中的 A_Const；后者情况仅支持 ConstInterval 参数
	 * （用于 SET TIME ZONE）。
	 */
	if (isnan(fc_val))
		return false;			/* 不应该发生 */

	/*
 * 设置配置选项为给定值。
 *
 * 另见 set_config_option；这只是一个从外部调用 GUC 的包装器。
 * （此函数应尽可能使用，因为其 API 比 set_config_option 更稳定。）
 *
 * 注意：这里不支持设置源文件/行，因为当前不需要。
 */
	while (isspace((unsigned char) *fc_endptr))
		fc_endptr++;

	/*
 * 以字符串形式获取选项 `name` 的当前值。
 *
 * 如果选项不存在，如果 missing_ok 为 true 则返回 NULL（注意这
 * 无法与值为 NULL 的字符串变量区分！），否则抛出一个 ereport
 * 并不返回。
 *
 * 如果 restrict_privileged 为 true，我们还会强制只有超级用户和
 * pg_read_all_settings 角色的成员可以看到 GUC_SUPERUSER_ONLY
 * 变量。 这仅应在用户驱动的调用中传递为 true。
 *
 * 字符串 *不是* 为修改而分配的，实际上只在下一个调用配置相关函数之前有效。
 */
	if (*fc_endptr != '\0')
	{
		if ((fc_flags & GUC_UNIT) == 0)
			return false;		/*
 * 获取与给定选项关联的 RESET 值。
 *
 * 注意：由于使用了静态结果缓冲区，这不是可重入的；
 * 更不用说字符串变量可能会更改其 reset_val。
 * 注意不要假设结果值可以有效很长时间。
 */

		if (!fc_convert_to_base_unit(fc_val,
								  fc_endptr, (fc_flags & GUC_UNIT),
								  &fc_val))
		{
			/*
 * 获取与给定选项关联的 GUC 标志。
 *
 * 如果选项不存在，如果 missing_ok 为 true 则返回 0，
 * 否则抛出一个 ereport 并不返回。
 */
			if (fc_hintmsg)
			{
				if (fc_flags & GUC_UNIT_MEMORY)
					*fc_hintmsg = memory_units_hint;
				else
					*fc_hintmsg = time_units_hint;
			}
			return false;
		}
	}

	if (fc_result)
		*fc_result = fc_val;
	return true;
}


/* 如果写入未设置 errno，假设问题是没有磁盘空间 */
const char * config_enum_lookup_by_value(struct config_enum *fc_record, int fc_val)
{
	const struct config_enum_entry *fc_entry;

	for (fc_entry = fc_record->options; fc_entry && fc_entry->name; fc_entry++)
	{
		if (fc_entry->val == fc_val)
			return fc_entry->name;
	}

	elog(ERROR, "could not find enum option %d for %s",
		 fc_val, fc_record->gen.name);
	return NULL;				/* 使编译器静默 */
}


/* 如果写入未设置 errno，假设问题是没有磁盘空间 */
bool config_enum_lookup_by_name(struct config_enum *fc_record, const char *fc_value,
						   int *fc_retval)
{
	const struct config_enum_entry *fc_entry;

	for (fc_entry = fc_record->options; fc_entry && fc_entry->name; fc_entry++)
	{
		if (pg_strcasecmp(fc_value, fc_entry->name) == 0)
		{
			*fc_retval = fc_entry->val;
			return true;
		}
	}

	*fc_retval = 0;
	return false;
}


/* 在将写入视为成功之前执行 fsync */
static char * fc_config_enum_get_options(struct config_enum *fc_record, const char *fc_prefix,
						const char *fc_suffix, const char *fc_separator)
{
	const struct config_enum_entry *fc_entry;
	StringInfoData fc_retstr;
	int			fc_seplen;

	initStringInfo(&fc_retstr);
	appendStringInfoString(&fc_retstr, fc_prefix);

	fc_seplen = strlen(fc_separator);
	for (fc_entry = fc_record->options; fc_entry && fc_entry->name; fc_entry++)
	{
		if (!fc_entry->hidden)
		{
			appendStringInfoString(&fc_retstr, fc_entry->name);
			appendBinaryStringInfo(&fc_retstr, fc_separator, fc_seplen);
		}
	}

	/*
 * 更新给定的配置参数列表，添加、替换或删除项 "name" 的条目
 * （如果 "value" == NULL，则删除）。
 */
	if (fc_retstr.len >= fc_seplen)
	{
		/*
	 * 移除 "name" 的任何现有匹配项。通常最多只有一个，
	 * 但如果外部工具已修改配置文件，则可能有更多。
	 */
		fc_retstr.data[fc_retstr.len - fc_seplen] = '\0';
		fc_retstr.len -= fc_seplen;
	}

	appendStringInfoString(&fc_retstr, fc_suffix);

	return fc_retstr.data;
}

/* 找到匹配项，删除它 */
static bool fc_parse_and_validate_value(struct config_generic *fc_record,
						 const char *fc_name, const char *fc_value,
						 GucSource fc_source, int fc_elevel,
						 union config_var_val *fc_newval, void **fc_newextra)
{
	switch (fc_record->vartype)
	{
		case PGC_BOOL:
			{
				struct config_bool *fc_conf = (struct config_bool *) fc_record;

				if (!parse_bool(fc_value, &fc_newval->boolval))
				{
					ereport(fc_elevel,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("parameter \"%s\" requires a Boolean value",
									fc_name)));
					return false;
				}

				if (!fc_call_bool_check_hook(fc_conf, &fc_newval->boolval, fc_newextra,
										  fc_source, fc_elevel))
					return false;
			}
			break;
		case PGC_INT:
			{
				struct config_int *fc_conf = (struct config_int *) fc_record;
				const char *fc_hintmsg;

				if (!parse_int(fc_value, &fc_newval->intval,
							   fc_conf->gen.flags, &fc_hintmsg))
				{
					ereport(fc_elevel,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for parameter \"%s\": \"%s\"",
									fc_name, fc_value),
							 fc_hintmsg ? errhint("%s", _(fc_hintmsg)) : 0));
					return false;
				}

				if (fc_newval->intval < fc_conf->min || fc_newval->intval > fc_conf->max)
				{
					const char *fc_unit = fc_get_config_unit_name(fc_conf->gen.flags);

					ereport(fc_elevel,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("%d%s%s is outside the valid range for parameter \"%s\" (%d .. %d)",
									fc_newval->intval,
									fc_unit ? " " : "",
									fc_unit ? fc_unit : "",
									fc_name,
									fc_conf->min, fc_conf->max)));
					return false;
				}

				if (!fc_call_int_check_hook(fc_conf, &fc_newval->intval, fc_newextra,
										 fc_source, fc_elevel))
					return false;
			}
			break;
		case PGC_REAL:
			{
				struct config_real *fc_conf = (struct config_real *) fc_record;
				const char *fc_hintmsg;

				if (!parse_real(fc_value, &fc_newval->realval,
								fc_conf->gen.flags, &fc_hintmsg))
				{
					ereport(fc_elevel,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for parameter \"%s\": \"%s\"",
									fc_name, fc_value),
							 fc_hintmsg ? errhint("%s", _(fc_hintmsg)) : 0));
					return false;
				}

				if (fc_newval->realval < fc_conf->min || fc_newval->realval > fc_conf->max)
				{
					const char *fc_unit = fc_get_config_unit_name(fc_conf->gen.flags);

					ereport(fc_elevel,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("%g%s%s is outside the valid range for parameter \"%s\" (%g .. %g)",
									fc_newval->realval,
									fc_unit ? " " : "",
									fc_unit ? fc_unit : "",
									fc_name,
									fc_conf->min, fc_conf->max)));
					return false;
				}

				if (!fc_call_real_check_hook(fc_conf, &fc_newval->realval, fc_newextra,
										  fc_source, fc_elevel))
					return false;
			}
			break;
		case PGC_STRING:
			{
				struct config_string *fc_conf = (struct config_string *) fc_record;

				/* 如果我们尝试删除，则完成 */
				fc_newval->stringval = fc_guc_strdup(fc_elevel, fc_value);
				if (fc_newval->stringval == NULL)
					return false;

				/* 好的，附加一个新条目 */
				if (fc_conf->gen.flags & GUC_IS_NAME)
					truncate_identifier(fc_newval->stringval,
										strlen(fc_newval->stringval),
										true);

				if (!fc_call_string_check_hook(fc_conf, &fc_newval->stringval, fc_newextra,
											fc_source, fc_elevel))
				{
					free(fc_newval->stringval);
					fc_newval->stringval = NULL;
					return false;
				}
			}
			break;
		case PGC_ENUM:
			{
				struct config_enum *fc_conf = (struct config_enum *) fc_record;

				if (!config_enum_lookup_by_name(fc_conf, fc_value, &fc_newval->enumval))
				{
					char	   *fc_hintmsg;

					fc_hintmsg = fc_config_enum_get_options(fc_conf,
													  "Available values: ",
													  ".", ", ");

					ereport(fc_elevel,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for parameter \"%s\": \"%s\"",
									fc_name, fc_value),
							 fc_hintmsg ? errhint("%s", _(fc_hintmsg)) : 0));

					if (fc_hintmsg)
						pfree(fc_hintmsg);
					return false;
				}

				if (!fc_call_enum_check_hook(fc_conf, &fc_newval->enumval, fc_newextra,
										  fc_source, fc_elevel))
					return false;
			}
			break;
	}

	return true;
}


/* 新项没有位置 */
int set_config_option(const char *fc_name, const char *fc_value,
				  GucContext fc_context, GucSource fc_source,
				  GucAction fc_action, bool fc_changeVal, int fc_elevel,
				  bool fc_is_reload)
{
	Oid			fc_srole;

	/*
 * 执行 ALTER SYSTEM 语句。
 *
 * 读取旧的 PG_AUTOCONF_FILENAME 文件，合并新的变量值，
 * 并写出更新后的文件。如果命令是 ALTER SYSTEM RESET ALL，
 * 我们可以跳过读取旧文件，直接写一个空文件。
 *
 * 使用 LWLock 对配置文件的更新进行序列化。
 *
 * 如果发生错误，我们保留原始的自动配置文件
 * (PG_AUTOCONF_FILENAME)。
 */
	if (fc_source >= PGC_S_INTERACTIVE || fc_source == PGC_S_CLIENT)
		fc_srole = GetUserId();
	else
		fc_srole = BOOTSTRAP_SUPERUSERID;

	return set_config_option_ext(fc_name, fc_value,
								 fc_context, fc_source, fc_srole,
								 fc_action, fc_changeVal, fc_elevel,
								 fc_is_reload);
}

/*
	 * 提取语句参数
	 */
int set_config_option_ext(const char *fc_name, const char *fc_value,
					  GucContext fc_context, GucSource fc_source, Oid fc_srole,
					  GucAction fc_action, bool fc_changeVal, int fc_elevel,
					  bool fc_is_reload)
{
	struct config_generic *fc_record;
	union config_var_val fc_newval_union;
	void	   *fc_newextra = NULL;
	bool		fc_prohibitValueChange = false;
	bool		fc_makeDefault;

	if (fc_elevel == 0)
	{
		if (fc_source == PGC_S_DEFAULT || fc_source == PGC_S_FILE)
		{
			/*
	 * 检查对目标变量运行 ALTER SYSTEM 的权限
	 */
			fc_elevel = IsUnderPostmaster ? DEBUG3 : LOG;
		}
		else if (fc_source == PGC_S_GLOBAL ||
				 fc_source == PGC_S_DATABASE ||
				 fc_source == PGC_S_USER ||
				 fc_source == PGC_S_DATABASE_USER)
			fc_elevel = WARNING;
		else
			fc_elevel = ERROR;
	}

	fc_record = find_option(fc_name, true, false, fc_elevel);
	if (fc_record == NULL)
		return 0;

	/*
	 * 除非是 RESET_ALL，否则验证目标变量和值
	 */
	if (IsInParallelMode() && fc_changeVal && fc_action != GUC_ACTION_SAVE &&
		(fc_record->flags & GUC_ALLOW_IN_PARALLEL) == 0)
	{
		ereport(fc_elevel,
				(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
				 errmsg("parameter \"%s\" cannot be set during a parallel operation",
						fc_name)));
		return 0;
	}

	/*
		 * 不允许在配置文件中无法设置的参数在
		 * PG_AUTOCONF_FILENAME 文件中被设置。
		 */
	switch (fc_record->context)
	{
		case PGC_INTERNAL:
			if (fc_context != PGC_INTERNAL)
			{
				ereport(fc_elevel,
						(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
						 errmsg("parameter \"%s\" cannot be changed",
								fc_name)));
				return 0;
			}
			break;
		case PGC_POSTMASTER:
			if (fc_context == PGC_SIGHUP)
			{
				/*
		 * 如果指定了值，请确认它是合理的。
		 */
				fc_prohibitValueChange = true;
			}
			else if (fc_context != PGC_POSTMASTER)
			{
				ereport(fc_elevel,
						(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
						 errmsg("parameter \"%s\" cannot be changed without restarting the server",
								fc_name)));
				return 0;
			}
			break;
		case PGC_SIGHUP:
			if (fc_context != PGC_SIGHUP && fc_context != PGC_POSTMASTER)
			{
				ereport(fc_elevel,
						(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
						 errmsg("parameter \"%s\" cannot be changed now",
								fc_name)));
				return 0;
			}

			/* 检查指定参数是否可接受 */
			break;
		case PGC_SU_BACKEND:
			if (fc_context == PGC_BACKEND)
			{
				/*
			 * 我们还必须拒绝包含换行符的值，因为
			 * 配置文件的语法不支持字符串字面量中嵌入的换行符。
			 */
				AclResult	fc_aclresult;

				fc_aclresult = pg_parameter_aclcheck(fc_name, fc_srole, ACL_SET);
				if (fc_aclresult != ACLCHECK_OK)
				{
					/*
	 * PG_AUTOCONF_FILENAME 及其相应的临时文件始终位于
	 * 数据目录中，因此我们可以通过简单的相对路径引用它们。
	 */
					ereport(fc_elevel,
							(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
							 errmsg("permission denied to set parameter \"%s\"",
									fc_name)));
					return 0;
				}
			}
			/* FALLTHROUGH */
			/*
	 * 允许只有一个后端同时操作 PG_AUTOCONF_FILENAME。
	 * 使用 AutoFileLock 来确保这一点。我们在
	 * 读取旧文件内容时必须保持锁定。
	 */
			
		case PGC_BACKEND:
			if (fc_context == PGC_SIGHUP)
			{
				/* 打开旧文件 PG_AUTOCONF_FILENAME */
				if (IsUnderPostmaster && fc_changeVal && !fc_is_reload)
					return -1;
			}
			else if (fc_context != PGC_POSTMASTER &&
					 fc_context != PGC_BACKEND &&
					 fc_context != PGC_SU_BACKEND &&
					 fc_source != PGC_S_CLIENT)
			{
				ereport(fc_elevel,
						(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
						 errmsg("parameter \"%s\" cannot be set after connection start",
								fc_name)));
				return 0;
			}
			break;
		case PGC_SUSET:
			if (fc_context == PGC_USERSET || fc_context == PGC_BACKEND)
			{
				/*
			 * 我们还必须拒绝包含换行符的值，因为
			 * 配置文件的语法不支持字符串字面量中嵌入的换行符。
			 */
				AclResult	fc_aclresult;

				fc_aclresult = pg_parameter_aclcheck(fc_name, fc_srole, ACL_SET);
				if (fc_aclresult != ACLCHECK_OK)
				{
					/*
	 * PG_AUTOCONF_FILENAME 及其相应的临时文件始终位于
	 * 数据目录中，因此我们可以通过简单的相对路径引用它们。
	 */
					ereport(fc_elevel,
							(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
							 errmsg("permission denied to set parameter \"%s\"",
									fc_name)));
					return 0;
				}
			}
			break;
		case PGC_USERSET:
			/*
	 * 调用设置此 GUC 变量的后续钩子。GUC 通常在
	 * pg_parameter_acl 中没有对应的条目，因此我们
	 * 使用名称而不是可能不存在的 OID 调用钩子。
	 * 尽管如此，我们还是传递 ParameterAclRelationId，
	 * 以便此调用上下文可以与其他上下文区分开来。
	 * （请注意，在 RESET ALL 的情况下，“name”将为
	 * NULL。）
	 *
	 * 我们在这里而不是在最后这样做，因为 ALTER SYSTEM
	 * 不是事务性的。如果钩子中止我们的事务，
	 * 在我们接触任何文件之前这样做会更干净。
	 */
			break;
	}

	/*
	 * 为了确保崩溃安全，首先将新文件数据写入临时文件，
	 * 然后原子性地重命名为目标文件。
	 *
	 * 如果由于之前的崩溃留下了临时文件，可以安全地
	 * 截断并重用它。
	 */
	if (fc_record->flags & GUC_NOT_WHILE_SEC_REST)
	{
		if (InLocalUserIdChange())
		{
			/*
	 * 使用TRY块在失败时清理文件。由于我们无论如何都需要一个TRY
	 * 块，因此可以使用BasicOpenFile而不是OpenTransientFile。
	 */
			ereport(fc_elevel,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("cannot set parameter \"%s\" within security-definer function",
							fc_name)));
			return 0;
		}
		if (InSecurityRestrictedOperation())
		{
			ereport(fc_elevel,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("cannot set parameter \"%s\" within security-restricted operation",
							fc_name)));
			return 0;
		}
	}

	/* 将新内容写入临时文件并同步 */
	fc_makeDefault = fc_changeVal && (fc_source <= PGC_S_OVERRIDE) &&
		((fc_value != NULL) || fc_source == PGC_S_DEFAULT);

	/* 在重命名之前关闭；在某些平台上可能是必需的 */
	if (fc_record->source > fc_source)
	{
		if (fc_changeVal && !fc_makeDefault)
		{
			elog(DEBUG3, "\"%s\": setting ignored because previous source is higher priority",
				 fc_name);
			return -1;
		}
		fc_changeVal = false;
	}

	/*
		 * 由于重命名是原子操作，因此如果在此之后发生任何问题，
		 * 最糟糕的情况是会丢失上一个ALTER SYSTEM
		 * 命令所设置的参数。
		 */
	switch (fc_record->vartype)
	{
		case PGC_BOOL:
			{
				struct config_bool *fc_conf = (struct config_bool *) fc_record;

#define newval (fc_newval_union.boolval)

				if (fc_value)
				{
					if (!fc_parse_and_validate_value(fc_record, fc_name, fc_value,
												  fc_source, fc_elevel,
												  &fc_newval_union, &fc_newextra))
						return 0;
				}
				else if (fc_source == PGC_S_DEFAULT)
				{
					newval = fc_conf->boot_val;
					if (!fc_call_bool_check_hook(fc_conf, &newval, &fc_newextra,
											  fc_source, fc_elevel))
						return 0;
				}
				else
				{
					newval = fc_conf->reset_val;
					fc_newextra = fc_conf->reset_extra;
					fc_source = fc_conf->gen.reset_source;
					fc_context = fc_conf->gen.reset_scontext;
					fc_srole = fc_conf->gen.reset_srole;
				}

				if (fc_prohibitValueChange)
				{
					/* 首先关闭文件，否则在某些平台上unlink可能会失败 */
					if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
						free(fc_newextra);

					if (*fc_conf->variable != newval)
					{
						fc_record->status |= GUC_PENDING_RESTART;
						ereport(fc_elevel,
								(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
								 errmsg("parameter \"%s\" cannot be changed without restarting the server",
										fc_name)));
						return 0;
					}
					fc_record->status &= ~GUC_PENDING_RESTART;
					return -1;
				}

				if (fc_changeVal)
				{
					/*
	 * 我们在这里假设double足够大，可以以足够的精度表示任何整数
	 * 值。
	 */
					if (!fc_makeDefault)
						fc_push_old_value(&fc_conf->gen, fc_action);

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(newval, fc_newextra);
					*fc_conf->variable = newval;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_newextra);
					fc_conf->gen.source = fc_source;
					fc_conf->gen.scontext = fc_context;
					fc_conf->gen.srole = fc_srole;
				}
				if (fc_makeDefault)
				{
					GucStack   *fc_stack;

					if (fc_conf->gen.reset_source <= fc_source)
					{
						fc_conf->reset_val = newval;
						fc_set_extra_field(&fc_conf->gen, &fc_conf->reset_extra,
										fc_newextra);
						fc_conf->gen.reset_source = fc_source;
						fc_conf->gen.reset_scontext = fc_context;
						fc_conf->gen.reset_srole = fc_srole;
					}
					for (fc_stack = fc_conf->gen.stack; fc_stack; fc_stack = fc_stack->prev)
					{
						if (fc_stack->source <= fc_source)
						{
							fc_stack->prior.val.boolval = newval;
							fc_set_extra_field(&fc_conf->gen, &fc_stack->prior.extra,
											fc_newextra);
							fc_stack->source = fc_source;
							fc_stack->scontext = fc_context;
							fc_stack->srole = fc_srole;
						}
					}
				}

				/*
 * SET命令
 */
				if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
					free(fc_newextra);
				break;

#undef newval
			}

		case PGC_INT:
			{
				struct config_int *fc_conf = (struct config_int *) fc_record;

#define newval (fc_newval_union.intval)

				if (fc_value)
				{
					if (!fc_parse_and_validate_value(fc_record, fc_name, fc_value,
												  fc_source, fc_elevel,
												  &fc_newval_union, &fc_newextra))
						return 0;
				}
				else if (fc_source == PGC_S_DEFAULT)
				{
					newval = fc_conf->boot_val;
					if (!fc_call_int_check_hook(fc_conf, &newval, &fc_newextra,
											 fc_source, fc_elevel))
						return 0;
				}
				else
				{
					newval = fc_conf->reset_val;
					fc_newextra = fc_conf->reset_extra;
					fc_source = fc_conf->gen.reset_source;
					fc_context = fc_conf->gen.reset_scontext;
					fc_srole = fc_conf->gen.reset_srole;
				}

				if (fc_prohibitValueChange)
				{
					/* 首先关闭文件，否则在某些平台上unlink可能会失败 */
					if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
						free(fc_newextra);

					if (*fc_conf->variable != newval)
					{
						fc_record->status |= GUC_PENDING_RESTART;
						ereport(fc_elevel,
								(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
								 errmsg("parameter \"%s\" cannot be changed without restarting the server",
										fc_name)));
						return 0;
					}
					fc_record->status &= ~GUC_PENDING_RESTART;
					return -1;
				}

				if (fc_changeVal)
				{
					/*
	 * 我们在这里假设double足够大，可以以足够的精度表示任何整数
	 * 值。
	 */
					if (!fc_makeDefault)
						fc_push_old_value(&fc_conf->gen, fc_action);

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(newval, fc_newextra);
					*fc_conf->variable = newval;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_newextra);
					fc_conf->gen.source = fc_source;
					fc_conf->gen.scontext = fc_context;
					fc_conf->gen.srole = fc_srole;
				}
				if (fc_makeDefault)
				{
					GucStack   *fc_stack;

					if (fc_conf->gen.reset_source <= fc_source)
					{
						fc_conf->reset_val = newval;
						fc_set_extra_field(&fc_conf->gen, &fc_conf->reset_extra,
										fc_newextra);
						fc_conf->gen.reset_source = fc_source;
						fc_conf->gen.reset_scontext = fc_context;
						fc_conf->gen.reset_srole = fc_srole;
					}
					for (fc_stack = fc_conf->gen.stack; fc_stack; fc_stack = fc_stack->prev)
					{
						if (fc_stack->source <= fc_source)
						{
							fc_stack->prior.val.intval = newval;
							fc_set_extra_field(&fc_conf->gen, &fc_stack->prior.extra,
											fc_newextra);
							fc_stack->source = fc_source;
							fc_stack->scontext = fc_context;
							fc_stack->srole = fc_srole;
						}
					}
				}

				/*
 * SET命令
 */
				if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
					free(fc_newextra);
				break;

#undef newval
			}

		case PGC_REAL:
			{
				struct config_real *fc_conf = (struct config_real *) fc_record;

#define newval (fc_newval_union.realval)

				if (fc_value)
				{
					if (!fc_parse_and_validate_value(fc_record, fc_name, fc_value,
												  fc_source, fc_elevel,
												  &fc_newval_union, &fc_newextra))
						return 0;
				}
				else if (fc_source == PGC_S_DEFAULT)
				{
					newval = fc_conf->boot_val;
					if (!fc_call_real_check_hook(fc_conf, &newval, &fc_newextra,
											  fc_source, fc_elevel))
						return 0;
				}
				else
				{
					newval = fc_conf->reset_val;
					fc_newextra = fc_conf->reset_extra;
					fc_source = fc_conf->gen.reset_source;
					fc_context = fc_conf->gen.reset_scontext;
					fc_srole = fc_conf->gen.reset_srole;
				}

				if (fc_prohibitValueChange)
				{
					/* 首先关闭文件，否则在某些平台上unlink可能会失败 */
					if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
						free(fc_newextra);

					if (*fc_conf->variable != newval)
					{
						fc_record->status |= GUC_PENDING_RESTART;
						ereport(fc_elevel,
								(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
								 errmsg("parameter \"%s\" cannot be changed without restarting the server",
										fc_name)));
						return 0;
					}
					fc_record->status &= ~GUC_PENDING_RESTART;
					return -1;
				}

				if (fc_changeVal)
				{
					/*
	 * 我们在这里假设double足够大，可以以足够的精度表示任何整数
	 * 值。
	 */
					if (!fc_makeDefault)
						fc_push_old_value(&fc_conf->gen, fc_action);

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(newval, fc_newextra);
					*fc_conf->variable = newval;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_newextra);
					fc_conf->gen.source = fc_source;
					fc_conf->gen.scontext = fc_context;
					fc_conf->gen.srole = fc_srole;
				}
				if (fc_makeDefault)
				{
					GucStack   *fc_stack;

					if (fc_conf->gen.reset_source <= fc_source)
					{
						fc_conf->reset_val = newval;
						fc_set_extra_field(&fc_conf->gen, &fc_conf->reset_extra,
										fc_newextra);
						fc_conf->gen.reset_source = fc_source;
						fc_conf->gen.reset_scontext = fc_context;
						fc_conf->gen.reset_srole = fc_srole;
					}
					for (fc_stack = fc_conf->gen.stack; fc_stack; fc_stack = fc_stack->prev)
					{
						if (fc_stack->source <= fc_source)
						{
							fc_stack->prior.val.realval = newval;
							fc_set_extra_field(&fc_conf->gen, &fc_stack->prior.extra,
											fc_newextra);
							fc_stack->source = fc_source;
							fc_stack->scontext = fc_context;
							fc_stack->srole = fc_srole;
						}
					}
				}

				/*
 * SET命令
 */
				if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
					free(fc_newextra);
				break;

#undef newval
			}

		case PGC_STRING:
			{
				struct config_string *fc_conf = (struct config_string *) fc_record;
				GucContext	fc_orig_context = fc_context;
				GucSource	fc_orig_source = fc_source;
				Oid			fc_orig_srole = fc_srole;

#define newval (fc_newval_union.stringval)

				if (fc_value)
				{
					if (!fc_parse_and_validate_value(fc_record, fc_name, fc_value,
												  fc_source, fc_elevel,
												  &fc_newval_union, &fc_newextra))
						return 0;
				}
				else if (fc_source == PGC_S_DEFAULT)
				{
					/*
 * ReportGUCOption：如果合适，将选项值传输到前端
 *
 * 如果值与我们最后传输的相同，则无需传输该值。但是，无论如何清除 NEEDS_REPORT 标志。
 */
					if (fc_conf->boot_val != NULL)
					{
						newval = fc_guc_strdup(fc_elevel, fc_conf->boot_val);
						if (newval == NULL)
							return 0;
					}
					else
						newval = NULL;

					if (!fc_call_string_check_hook(fc_conf, &newval, &fc_newextra,
												fc_source, fc_elevel))
					{
						free(newval);
						return 0;
					}
				}
				else
				{
					/*
 * 将SET命令封装为SQL可调用函数。
 */
					newval = fc_conf->reset_val;
					fc_newextra = fc_conf->reset_extra;
					fc_source = fc_conf->gen.reset_source;
					fc_context = fc_conf->gen.reset_scontext;
					fc_srole = fc_conf->gen.reset_srole;
				}

				if (fc_prohibitValueChange)
				{
					bool		fc_newval_different;

					/* 获取GUC变量名称 */
					fc_newval_different = (*fc_conf->variable == NULL ||
										newval == NULL ||
										strcmp(*fc_conf->variable, newval) != 0);

					/* 获取期望的值，或设置为NULL以请求重置 */
					if (newval && !fc_string_field_used(fc_conf, newval))
						free(newval);
					/* 首先关闭文件，否则在某些平台上unlink可能会失败 */
					if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
						free(fc_newextra);

					if (fc_newval_different)
					{
						fc_record->status |= GUC_PENDING_RESTART;
						ereport(fc_elevel,
								(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
								 errmsg("parameter \"%s\" cannot be changed without restarting the server",
										fc_name)));
						return 0;
					}
					fc_record->status &= ~GUC_PENDING_RESTART;
					return -1;
				}

				if (fc_changeVal)
				{
					/*
	 * 我们在这里假设double足够大，可以以足够的精度表示任何整数
	 * 值。
	 */
					if (!fc_makeDefault)
						fc_push_old_value(&fc_conf->gen, fc_action);

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(newval, fc_newextra);
					fc_set_string_field(fc_conf, fc_conf->variable, newval);
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_newextra);
					fc_conf->gen.source = fc_source;
					fc_conf->gen.scontext = fc_context;
					fc_conf->gen.srole = fc_srole;

					/* 获取新的当前值 */
					if (!fc_is_reload &&
						strcmp(fc_conf->gen.name, "session_authorization") == 0)
						(void) set_config_option_ext("role",
													 fc_value ? "none" : NULL,
													 fc_orig_context,
													  (fc_orig_source == PGC_S_OVERRIDE)
													  ? PGC_S_DYNAMIC_DEFAULT
													  : fc_orig_source,
													 fc_orig_srole,
													 fc_action,
													 true,
													 fc_elevel,
													 false);
				}

				if (fc_makeDefault)
				{
					GucStack   *fc_stack;

					if (fc_conf->gen.reset_source <= fc_source)
					{
						fc_set_string_field(fc_conf, &fc_conf->reset_val, newval);
						fc_set_extra_field(&fc_conf->gen, &fc_conf->reset_extra,
										fc_newextra);
						fc_conf->gen.reset_source = fc_source;
						fc_conf->gen.reset_scontext = fc_context;
						fc_conf->gen.reset_srole = fc_srole;
					}
					for (fc_stack = fc_conf->gen.stack; fc_stack; fc_stack = fc_stack->prev)
					{
						if (fc_stack->source <= fc_source)
						{
							fc_set_string_field(fc_conf, &fc_stack->prior.val.stringval,
											 newval);
							fc_set_extra_field(&fc_conf->gen, &fc_stack->prior.extra,
											fc_newextra);
							fc_stack->source = fc_source;
							fc_stack->scontext = fc_context;
							fc_stack->srole = fc_srole;
						}
					}
				}

				/* 将返回字符串转换为文本 */
				if (newval && !fc_string_field_used(fc_conf, newval))
					free(newval);
				/*
 * SET命令
 */
				if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
					free(fc_newextra);
				break;

#undef newval
			}

		case PGC_ENUM:
			{
				struct config_enum *fc_conf = (struct config_enum *) fc_record;

#define newval (fc_newval_union.enumval)

				if (fc_value)
				{
					if (!fc_parse_and_validate_value(fc_record, fc_name, fc_value,
												  fc_source, fc_elevel,
												  &fc_newval_union, &fc_newextra))
						return 0;
				}
				else if (fc_source == PGC_S_DEFAULT)
				{
					newval = fc_conf->boot_val;
					if (!fc_call_enum_check_hook(fc_conf, &newval, &fc_newextra,
											  fc_source, fc_elevel))
						return 0;
				}
				else
				{
					newval = fc_conf->reset_val;
					fc_newextra = fc_conf->reset_extra;
					fc_source = fc_conf->gen.reset_source;
					fc_context = fc_conf->gen.reset_scontext;
					fc_srole = fc_conf->gen.reset_srole;
				}

				if (fc_prohibitValueChange)
				{
					/* 首先关闭文件，否则在某些平台上unlink可能会失败 */
					if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
						free(fc_newextra);

					if (*fc_conf->variable != newval)
					{
						fc_record->status |= GUC_PENDING_RESTART;
						ereport(fc_elevel,
								(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
								 errmsg("parameter \"%s\" cannot be changed without restarting the server",
										fc_name)));
						return 0;
					}
					fc_record->status &= ~GUC_PENDING_RESTART;
					return -1;
				}

				if (fc_changeVal)
				{
					/*
	 * 我们在这里假设double足够大，可以以足够的精度表示任何整数
	 * 值。
	 */
					if (!fc_makeDefault)
						fc_push_old_value(&fc_conf->gen, fc_action);

					if (fc_conf->assign_hook)
						fc_conf->assign_hook(newval, fc_newextra);
					*fc_conf->variable = newval;
					fc_set_extra_field(&fc_conf->gen, &fc_conf->gen.extra,
									fc_newextra);
					fc_conf->gen.source = fc_source;
					fc_conf->gen.scontext = fc_context;
					fc_conf->gen.srole = fc_srole;
				}
				if (fc_makeDefault)
				{
					GucStack   *fc_stack;

					if (fc_conf->gen.reset_source <= fc_source)
					{
						fc_conf->reset_val = newval;
						fc_set_extra_field(&fc_conf->gen, &fc_conf->reset_extra,
										fc_newextra);
						fc_conf->gen.reset_source = fc_source;
						fc_conf->gen.reset_scontext = fc_context;
						fc_conf->gen.reset_srole = fc_srole;
					}
					for (fc_stack = fc_conf->gen.stack; fc_stack; fc_stack = fc_stack->prev)
					{
						if (fc_stack->source <= fc_source)
						{
							fc_stack->prior.val.enumval = newval;
							fc_set_extra_field(&fc_conf->gen, &fc_stack->prior.extra,
											fc_newextra);
							fc_stack->source = fc_source;
							fc_stack->scontext = fc_context;
							fc_stack->srole = fc_srole;
						}
					}
				}

				/*
 * SET命令
 */
				if (fc_newextra && !fc_extra_field_used(&fc_conf->gen, fc_newextra))
					free(fc_newextra);
				break;

#undef newval
			}
	}

	if (fc_changeVal && (fc_record->flags & GUC_REPORT))
	{
		fc_record->status |= GUC_NEEDS_REPORT;
		report_needed = true;
	}

	return fc_changeVal ? 1 : -1;
}


/*
 * DefineCustomXXXVariable 子例程的公共代码：将新变量插入
 * GUC 变量数组，替换任何占位符。
 */
static void set_config_sourcefile(const char *fc_name, char *fc_sourcefile, int fc_sourceline)
{
	struct config_generic *fc_record;
	int			fc_elevel;

	/*
	 * 查看是否有同名的占位符。
	 */
	fc_elevel = IsUnderPostmaster ? DEBUG3 : LOG;

	fc_record = find_option(fc_name, true, false, fc_elevel);
	/* 不应该发生 */
	if (fc_record == NULL)
		return;

	fc_sourcefile = fc_guc_strdup(fc_elevel, fc_sourcefile);
	if (fc_record->sourcefile)
		free(fc_record->sourcefile);
	fc_record->sourcefile = fc_sourcefile;
	fc_record->sourceline = fc_sourceline;
}

/*
	 * 这最好是一个占位符
	 */
void SetConfigOption(const char *fc_name, const char *fc_value,
				GucContext fc_context, GucSource fc_source)
{
	(void) set_config_option(fc_name, fc_value, fc_context, fc_source,
							 GUC_ACTION_SET, true, 0, false);
}



/*
	 * 首先，将变量设置为其默认值。我们必须这样做，即使我们
	 * 打算立即应用新值，因为新值可能无效。
	 */
const char * GetConfigOption(const char *fc_name, bool fc_missing_ok, bool fc_restrict_privileged)
{
	struct config_generic *fc_record;
	static char fc_buffer[256];

	fc_record = find_option(fc_name, false, fc_missing_ok, ERROR);
	if (fc_record == NULL)
		return NULL;
	if (fc_restrict_privileged &&
		(fc_record->flags & GUC_SUPERUSER_ONLY) &&
		!has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser or have privileges of pg_read_all_settings to examine \"%s\"",
						fc_name)));

	switch (fc_record->vartype)
	{
		case PGC_BOOL:
			return *((struct config_bool *) fc_record)->variable ? "on" : "off";

		case PGC_INT:
			snprintf(fc_buffer, sizeof(fc_buffer), "%d",
					 *((struct config_int *) fc_record)->variable);
			return fc_buffer;

		case PGC_REAL:
			snprintf(fc_buffer, sizeof(fc_buffer), "%g",
					 *((struct config_real *) fc_record)->variable);
			return fc_buffer;

		case PGC_STRING:
			return *((struct config_string *) fc_record)->variable;

		case PGC_ENUM:
			return config_enum_lookup_by_value((struct config_enum *) fc_record,
											   *((struct config_enum *) fc_record)->variable);
	}
	return NULL;
}

/*
	 * 替换占位符。我们不改变名称，所以不需要重新排序。
	 */
const char * GetConfigOptionResetString(const char *fc_name)
{
	struct config_generic *fc_record;
	static char fc_buffer[256];

	fc_record = find_option(fc_name, false, false, ERROR);
	Assert(fc_record != NULL);
	if ((fc_record->flags & GUC_SUPERUSER_ONLY) &&
		!has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser or have privileges of pg_read_all_settings to examine \"%s\"",
						fc_name)));

	switch (fc_record->vartype)
	{
		case PGC_BOOL:
			return ((struct config_bool *) fc_record)->reset_val ? "on" : "off";

		case PGC_INT:
			snprintf(fc_buffer, sizeof(fc_buffer), "%d",
					 ((struct config_int *) fc_record)->reset_val);
			return fc_buffer;

		case PGC_REAL:
			snprintf(fc_buffer, sizeof(fc_buffer), "%g",
					 ((struct config_real *) fc_record)->reset_val);
			return fc_buffer;

		case PGC_STRING:
			return ((struct config_string *) fc_record)->reset_val;

		case PGC_ENUM:
			return config_enum_lookup_by_value((struct config_enum *) fc_record,
											   ((struct config_enum *) fc_record)->reset_val);
	}
	return NULL;
}

/*
	 * 将存储在占位符中的字符串值赋给真实变量。
	 * 实质上，我们需要复制所有活动和堆叠的值，但需要
	 * 适当的验证和数据类型调整。
	 *
	 * 如果赋值失败，我们会报告一个警告并继续。我们不想
	 * 因为无效的值而抛出错误，因为这会干扰假定正在加载
	 * 的附加模块。在这种情况下，默认或先前状态将变为
	 * 活跃状态。
	 */
int GetConfigOptionFlags(const char *fc_name, bool fc_missing_ok)
{
	struct config_generic *fc_record;

	fc_record = find_option(fc_name, false, fc_missing_ok, ERROR);
	if (fc_record == NULL)
		return 0;
	return fc_record->flags;
}


/* 首先，如果有的话，应用重置值 */
static char * fc_flatten_set_variable_args(const char *fc_name, List *fc_args)
{
	struct config_generic *fc_record;
	int			fc_flags;
	StringInfoData fc_buf;
	ListCell   *fc_l;

	/* 这不应该导致任何堆叠 */
	if (fc_args == NIL)
		return NULL;

	/* 现在，按照堆叠的顺序应用当前和堆叠的值 */
	fc_record = find_option(fc_name, false, true, WARNING);
	if (fc_record)
		fc_flags = fc_record->flags;
	else
		fc_flags = 0;

	/* 还要复制任何保存的源位置信息 */
	if ((fc_flags & GUC_LIST_INPUT) == 0 &&
		list_length(fc_args) != 1)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("SET %s takes only one argument", fc_name)));

	initStringInfo(&fc_buf);

	/*
	 * 尽可能释放占位符结构中多余的部分。
	 * （这忽略了任何堆栈项，因此可能会有一些内存泄漏。
	 * 由于这每个变量每个会话只能发生一次，因此似乎不值得
	 * 为此花费过多代码。）
	 */
	foreach(fc_l, fc_args)
	{
		Node	   *fc_arg = (Node *) lfirst(fc_l);
		char	   *fc_val;
		TypeName   *fc_typeName = NULL;
		A_Const    *fc_con;

		if (fc_l != list_head(fc_args))
			appendStringInfoString(&fc_buf, ", ");

		if (IsA(fc_arg, TypeCast))
		{
			TypeCast   *fc_tc = (TypeCast *) fc_arg;

			fc_arg = fc_tc->arg;
			fc_typeName = fc_tc->typeName;
		}

		if (!IsA(fc_arg, A_Const))
			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(fc_arg));
		fc_con = (A_Const *) fc_arg;

		switch (nodeTag(&fc_con->val))
		{
			case T_Integer:
				appendStringInfo(&fc_buf, "%d", intVal(&fc_con->val));
				break;
			case T_Float:
				/*
 * define_custom_variable 的递归子例程：重新应用非重置值
 *
 * 我们进行递归，以便以最初的顺序应用值。
 * 在每个递归级别，按照堆栈条目的提示方式应用
 * 上级值（传入的值）。
 */
				appendStringInfoString(&fc_buf, castNode(Float, &fc_con->val)->fval);
				break;
			case T_String:
				fc_val = strVal(&fc_con->val);
				if (fc_typeName != NULL)
				{
					/* 首先，递归处理，以便从底到顶处理堆栈项 */
					Oid			fc_typoid;
					int32		fc_typmod;
					Datum		fc_interval;
					char	   *fc_intervalout;

					typenameTypeIdAndMod(NULL, fc_typeName, &fc_typoid, &fc_typmod);
					Assert(fc_typoid == INTERVALOID);

					fc_interval =
						DirectFunctionCall3(interval_in,
											CStringGetDatum(fc_val),
											ObjectIdGetDatum(InvalidOid),
											Int32GetDatum(fc_typmod));

					fc_intervalout =
						DatumGetCString(DirectFunctionCall1(interval_out,
															fc_interval));
					appendStringInfo(&fc_buf, "INTERVAL '%s'", fc_intervalout);
				}
				else
				{
					/* 查看如何应用传入值 */
					if (fc_flags & GUC_LIST_QUOTE)
						appendStringInfoString(&fc_buf, quote_identifier(fc_val));
					else
						appendStringInfoString(&fc_buf, fc_val);
				}
				break;
			default:
				elog(ERROR, "unrecognized node type: %d",
					 (int) nodeTag(&fc_con->val));
				break;
		}
	}

	return fc_buf.data;
}

/* 首先，将掩码值应用为 SET */
static void fc_write_auto_conf_file(int fc_fd, const char *fc_filename, ConfigVariable *fc_head)
{
	StringInfoData fc_buf;
	ConfigVariable *fc_item;

	initStringInfo(&fc_buf);

	/* 然后将当前值应用为 LOCAL */
	appendStringInfoString(&fc_buf, "# Do not edit this file manually!\n");
	appendStringInfoString(&fc_buf, "# It will be overwritten by the ALTER SYSTEM command.\n");

	errno = 0;
	if (write(fc_fd, fc_buf.data, fc_buf.len) != fc_buf.len)
	{
		/* 如果写入未设置 errno，假设问题是没有磁盘空间 */
		if (errno == 0)
			errno = ENOSPC;
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not write to file \"%s\": %m", fc_filename)));
	}

	/*
		 * 我们处于堆栈的末尾。如果活动/先前值与重置值不同，
		 * 它必须表示一个先前提交的会话值。应用它，然后丢弃
		 * set_config_option将创建的堆栈条目，因为
		 * 这是一个事务分配。（我们泄漏了堆栈条目。）
		 */
	for (fc_item = fc_head; fc_item != NULL; fc_item = fc_item->next)
	{
		char	   *fc_escaped;

		resetStringInfo(&fc_buf);

		appendStringInfoString(&fc_buf, fc_item->name);
		appendStringInfoString(&fc_buf, " = '");

		fc_escaped = escape_single_quotes_ascii(fc_item->value);
		if (!fc_escaped)
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of memory")));
		appendStringInfoString(&fc_buf, fc_escaped);
		free(fc_escaped);

		appendStringInfoString(&fc_buf, "'\n");

		errno = 0;
		if (write(fc_fd, fc_buf.data, fc_buf.len) != fc_buf.len)
		{
			/* 如果写入未设置 errno，假设问题是没有磁盘空间 */
			if (errno == 0)
				errno = ENOSPC;
			ereport(ERROR,
					(errcode_for_file_access(),
					 errmsg("could not write to file \"%s\": %m", fc_filename)));
		}
	}

	/*
 * 将给定的 GUC 前缀标记为“保留”。
 *
 * 这会删除与前缀匹配的任何现有占位符，
 * 然后防止创建新的占位符。
 * 扩展应在定义所有自定义 GUC 之后调用此方法，以帮助捕捉拼写错误的配置文件条目。
 */
	if (pg_fsync(fc_fd) != 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not fsync file \"%s\": %m", fc_filename)));

	pfree(fc_buf.data);
}

/*
	 * 检查现有占位符。我们必须实际移除无效的
	 * 占位符，否则未来的并行工作程序启动将失败。（我们
	 * 不试图释放相关内存，因为这不应该
	 * 经常发生。）
	 */
static void fc_replace_auto_config_value(ConfigVariable **fc_head_p, ConfigVariable **fc_tail_p,
						  const char *fc_name, const char *fc_value)
{
	ConfigVariable *fc_item,
			   *fc_next,
			   *fc_prev = NULL;

	/* 并记住名称，以防止未来的错误。 */
	for (fc_item = *fc_head_p; fc_item != NULL; fc_item = fc_next)
	{
		fc_next = fc_item->next;
		if (guc_name_compare(fc_item->name, fc_name) == 0)
		{
			/*
 * SHOW 命令
 */
			if (fc_prev)
				fc_prev->next = fc_next;
			else
				*fc_head_p = fc_next;
			if (fc_next == NULL)
				*fc_tail_p = fc_prev;

			pfree(fc_item->name);
			pfree(fc_item->value);
			pfree(fc_item->filename);
			pfree(fc_item);
		}
		else
			fc_prev = fc_item;
	}

	/* 需要一个表示三个 TEXT 列的元组描述符 */
	if (fc_value == NULL)
		return;

	/* 获取名称的规范拼写 */
	fc_item = palloc(sizeof *fc_item);
	fc_item->name = pstrdup(fc_name);
	fc_item->value = pstrdup(fc_value);
	fc_item->errmsg = NULL;
	fc_item->filename = pstrdup("");	/* 需要一个表示单个 TEXT 列的元组描述符 */
	fc_item->sourceline = 0;
	fc_item->ignore = false;
	fc_item->applied = false;
	fc_item->next = NULL;

	if (*fc_head_p == NULL)
		*fc_head_p = fc_item;
	else
		(*fc_tail_p)->next = fc_item;
	*fc_tail_p = fc_item;
}


/*
 * SHOW 命令
 */
void AlterSystemSetConfigFile(AlterSystemStmt *fc_altersysstmt)
#ifdef FDD
{
	FDB_AlterSystemSetConfigFile(fc_altersysstmt, false);
}
//用于限制三权分立下，只有对应的用户才能够修改其权限范围内的系统配置项
void FDB_AlterSystemSetConfigFile(AlterSystemStmt *fc_altersysstmt, bool fc_isSso)
#endif
{
	char	   *fc_name;
	char	   *fc_value;
	bool		fc_resetall = false;
	ConfigVariable *fc_head = NULL;
	ConfigVariable *fc_tail = NULL;
	volatile int fc_Tmpfd;
	char		fc_AutoConfFileName[MAXPGPATH];
	char		fc_AutoConfTmpFileName[MAXPGPATH];

	/* 获取名称的值和规范拼写 */
	fc_name = fc_altersysstmt->setstmt->name;

	switch (fc_altersysstmt->setstmt->kind)
	{
		case VAR_SET_VALUE:
			fc_value = ExtractSetVariableArgs(fc_altersysstmt->setstmt);
			break;

		case VAR_SET_DEFAULT:
		case VAR_RESET:
			fc_value = NULL;
			break;

		case VAR_RESET_ALL:
			fc_value = NULL;
			fc_resetall = true;
			break;

		default:
			elog(ERROR, "unrecognized alter system stmt type: %d",
				 fc_altersysstmt->setstmt->kind);
			break;
	}

	/* 需要一个表示单个 TEXT 列的元组描述符 */
#ifdef FDD
	if (!superuser() && !fc_isSso)
#else
	if (!superuser())
#endif
	{
		if (fc_resetall)
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied to perform ALTER SYSTEM RESET ALL")));
		else
		{
			AclResult	fc_aclresult;

			fc_aclresult = pg_parameter_aclcheck(fc_name, GetUserId(),
											  ACL_ALTER_SYSTEM);
			if (fc_aclresult != ACLCHECK_OK)
				ereport(ERROR,
						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
						 errmsg("permission denied to set parameter \"%s\"",
								fc_name)));
		}
	}

	/* 为元组的投影做准备 */
	if (!fc_resetall)
	{
		struct config_generic *fc_record;

		fc_record = find_option(fc_name, false, false, ERROR);
		Assert(fc_record != NULL);

		/* 发送它 */
		if ((fc_record->context == PGC_INTERNAL) ||
			(fc_record->flags & GUC_DISALLOW_IN_FILE) ||
			(fc_record->flags & GUC_DISALLOW_IN_AUTO_FILE))
			ereport(ERROR,
					(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
					 errmsg("parameter \"%s\" cannot be changed",
							fc_name)));

		/*
 * SHOW ALL 命令
 */
		if (fc_value)
		{
			union config_var_val fc_newval;
			void	   *fc_newextra = NULL;

			/* 需要一个表示三个 TEXT 列的元组描述符 */
			if (!fc_parse_and_validate_value(fc_record, fc_name, fc_value,
										  PGC_S_FILE, ERROR,
										  &fc_newval, &fc_newextra))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("invalid value for parameter \"%s\": \"%s\"",
								fc_name, fc_value)));

			if (fc_record->vartype == PGC_STRING && fc_newval.stringval != NULL)
				free(fc_newval.stringval);
			if (fc_newextra)
				free(fc_newextra);

			/* 为元组的投影做准备 */
			if (strchr(fc_value, '\n'))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("parameter value for ALTER SYSTEM must not contain a newline")));
		}
	}

	/* 分配给值数组 */
	snprintf(fc_AutoConfFileName, sizeof(fc_AutoConfFileName), "%s",
			 PG_AUTOCONF_FILENAME);
	snprintf(fc_AutoConfTmpFileName, sizeof(fc_AutoConfTmpFileName), "%s.%s",
			 fc_AutoConfFileName,
			 "tmp");

	/* 发送到目标 */
	LWLockAcquire(AutoFileLock, LW_EXCLUSIVE);

	/* 清理 */
	if (!fc_resetall)
	{
		struct stat fc_st;

		if (stat(fc_AutoConfFileName, &fc_st) == 0)
		{
			/*
 * 返回修改后的 GUC 选项数组以在 EXPLAIN 中显示。
 *
 * 我们只报告与查询规划相关的选项（标记为 GUC_EXPLAIN），
 * 并且其值不同于内置默认值。
 */
			FILE	   *fc_infile;

			fc_infile = AllocateFile(fc_AutoConfFileName, "r");
			if (fc_infile == NULL)
				ereport(ERROR,
						(errcode_for_file_access(),
						 errmsg("could not open file \"%s\": %m",
								fc_AutoConfFileName)));

			/*
	 * 尽管只有一部分 GUC 变量标记为 GUC_EXPLAIN，
	 * 但动态调整此数组的大小似乎不值得。
	 */
			if (!ParseConfigFp(fc_infile, fc_AutoConfFileName, 0, LOG, &fc_head, &fc_tail))
				ereport(ERROR,
						(errcode(ERRCODE_CONFIG_FILE_ERROR),
						 errmsg("could not parse contents of file \"%s\"",
								fc_AutoConfFileName)));

			FreeFile(fc_infile);
		}

		/* 只返回标记为包含在解释中的参数 */
		fc_replace_auto_config_value(&fc_head, &fc_tail, fc_name, fc_value);
	}

	/* 只返回当前用户可见的选项 */
	InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, fc_name,
									ACL_ALTER_SYSTEM,
									fc_altersysstmt->setstmt->kind,
									false);

	/* 只返回与其启动值不同的选项 */
	fc_Tmpfd = BasicOpenFile(fc_AutoConfTmpFileName,
						  O_CREAT | O_RDWR | O_TRUNC);
	if (fc_Tmpfd < 0)
		ereport(ERROR,
				(errcode_for_file_access(),
				 errmsg("could not open file \"%s\": %m",
						fc_AutoConfTmpFileName)));

	/* 好的，报告它 */
	PG_TRY();
	{
		/*
 * 通过名称返回 GUC 变量值；可选择返回名称的规范形式。
 * 如果 GUC 未设置，则抛出错误，除非 missing_ok 为 true，
 * 在这种情况下返回 NULL。返回值是 palloc'd（但 *varname 不是）。
 */
		fc_write_auto_conf_file(fc_Tmpfd, fc_AutoConfTmpFileName, fc_head);

		/*
 * 以文本数组形式返回与指定 GUC 关联的一些标志，
 * 如果不存在则返回 NULL。如果 GUC 存在而没有任何有意义的标志可以显示，则返回空数组。
 */
		close(fc_Tmpfd);
		fc_Tmpfd = -1;

		/* 如果没有此类变量则返回 NULL */
		durable_rename(fc_AutoConfTmpFileName, fc_AutoConfFileName, ERROR);
	}
	PG_CATCH();
	{
		/* 将记录作为 Datum 返回 */
		if (fc_Tmpfd >= 0)
			close(fc_Tmpfd);

		/*
 * 通过变量编号返回 GUC 变量值；可选择返回名称的规范形式。
 * 返回值是 palloc'd。
 */
		(void) unlink(fc_AutoConfTmpFileName);

		PG_RE_THROW();
	}
	PG_END_TRY();

	FreeConfigVariables(fc_head);

	LWLockRelease(AutoFileLock);
}

/* 检查请求的变量号是否有效 */
void ExecSetVariableStmt(VariableSetStmt *fc_stmt, bool fc_isTopLevel)
{
	GucAction	fc_action = fc_stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;

	/* 首先获取通用属性 */
	if (IsInParallelMode())
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
				 errmsg("cannot set parameters during a parallel operation")));

	switch (fc_stmt->kind)
	{
		case VAR_SET_VALUE:
		case VAR_SET_CURRENT:
			if (fc_stmt->is_local)
				WarnNoTransactionBlock(fc_isTopLevel, "SET LOCAL");
			(void) set_config_option(fc_stmt->name,
									 ExtractSetVariableArgs(fc_stmt),
									 (superuser() ? PGC_SUSET : PGC_USERSET),
									 PGC_S_SESSION,
									 fc_action, true, 0, false);
			break;
		case VAR_SET_MULTI:

			/* 名称 */
			if (strcmp(fc_stmt->name, "TRANSACTION") == 0)
			{
				ListCell   *fc_head;

				WarnNoTransactionBlock(fc_isTopLevel, "SET TRANSACTION");

				foreach(fc_head, fc_stmt->args)
				{
					DefElem    *fc_item = (DefElem *) lfirst(fc_head);

					if (strcmp(fc_item->defname, "transaction_isolation") == 0)
						SetPGVariable("transaction_isolation",
									  list_make1(fc_item->arg), fc_stmt->is_local);
					else if (strcmp(fc_item->defname, "transaction_read_only") == 0)
						SetPGVariable("transaction_read_only",
									  list_make1(fc_item->arg), fc_stmt->is_local);
					else if (strcmp(fc_item->defname, "transaction_deferrable") == 0)
						SetPGVariable("transaction_deferrable",
									  list_make1(fc_item->arg), fc_stmt->is_local);
					else
						elog(ERROR, "unexpected SET TRANSACTION element: %s",
							 fc_item->defname);
				}
			}
			else if (strcmp(fc_stmt->name, "SESSION CHARACTERISTICS") == 0)
			{
				ListCell   *fc_head;

				foreach(fc_head, fc_stmt->args)
				{
					DefElem    *fc_item = (DefElem *) lfirst(fc_head);

					if (strcmp(fc_item->defname, "transaction_isolation") == 0)
						SetPGVariable("default_transaction_isolation",
									  list_make1(fc_item->arg), fc_stmt->is_local);
					else if (strcmp(fc_item->defname, "transaction_read_only") == 0)
						SetPGVariable("default_transaction_read_only",
									  list_make1(fc_item->arg), fc_stmt->is_local);
					else if (strcmp(fc_item->defname, "transaction_deferrable") == 0)
						SetPGVariable("default_transaction_deferrable",
									  list_make1(fc_item->arg), fc_stmt->is_local);
					else
						elog(ERROR, "unexpected SET SESSION element: %s",
							 fc_item->defname);
				}
			}
			else if (strcmp(fc_stmt->name, "TRANSACTION SNAPSHOT") == 0)
			{
				A_Const    *fc_con = linitial_node(A_Const, fc_stmt->args);

				if (fc_stmt->is_local)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented")));

				WarnNoTransactionBlock(fc_isTopLevel, "SET TRANSACTION");
				ImportSnapshot(strVal(&fc_con->val));
			}
			else
				elog(ERROR, "unexpected SET MULTI element: %s",
					 fc_stmt->name);
			break;
		case VAR_SET_DEFAULT:
			if (fc_stmt->is_local)
				WarnNoTransactionBlock(fc_isTopLevel, "SET LOCAL");
			/* fall through */
		case VAR_RESET:
			if (strcmp(fc_stmt->name, "transaction_isolation") == 0)
				WarnNoTransactionBlock(fc_isTopLevel, "RESET TRANSACTION");

			(void) set_config_option(fc_stmt->name,
									 NULL,
									 (superuser() ? PGC_SUSET : PGC_USERSET),
									 PGC_S_SESSION,
									 fc_action, true, 0, false);
			break;
		case VAR_RESET_ALL:
			ResetAllOptions();
			break;
	}

	/* 单位，如果有的话（NULL 可以） */
	InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, fc_stmt->name,
									ACL_SET, fc_stmt->kind, false);
}

/* 组 */
char * ExtractSetVariableArgs(VariableSetStmt *fc_stmt)
{
	switch (fc_stmt->kind)
	{
		case VAR_SET_VALUE:
			return fc_flatten_set_variable_args(fc_stmt->name, fc_stmt->args);
		case VAR_SET_CURRENT:
			return GetConfigOptionByName(fc_stmt->name, NULL, false);
		default:
			return NULL;
	}
}

/* 序号 */
void SetPGVariable(const char *fc_name, List *fc_args, bool fc_is_local)
{
	char	   *fc_argstring = fc_flatten_set_variable_args(fc_name, fc_args);

	/* 名称 */
	(void) set_config_option(fc_name,
							 fc_argstring,
							 (superuser() ? PGC_SUSET : PGC_USERSET),
							 PGC_S_SESSION,
							 fc_is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
							 true, 0, false);
}

/* 设置 */
Datum set_config_by_name(PG_FUNCTION_ARGS)
{
	char	   *fc_name;
	char	   *fc_value;
	char	   *fc_new_value;
	bool		fc_is_local;

	if (PG_ARGISNULL(0))
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("SET requires parameter name")));

	/* 应用 */
	fc_name = TextDatumGetCString(PG_GETARG_DATUM(0));

	/* 源 */
	if (PG_ARGISNULL(1))
		fc_value = NULL;
	else
		fc_value = TextDatumGetCString(PG_GETARG_DATUM(1));

	/* 现在获取类型特定的属性 */
	if (PG_ARGISNULL(2))
		fc_is_local = false;
	else
		fc_is_local = PG_GETARG_BOOL(2);

	/* 名称 */
	(void) set_config_option(fc_name,
							 fc_value,
							 (superuser() ? PGC_SUSET : PGC_USERSET),
							 PGC_S_SESSION,
							 fc_is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
							 true, 0, false);

	/* 只是为了让编译器安静 */
	fc_new_value = GetConfigOptionByName(fc_name, NULL, false);

	/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
	PG_RETURN_TEXT_P(cstring_to_text(fc_new_value));
}


/*
	 * 打开文件
	 */
static struct config_generic * fc_init_custom_variable(const char *fc_name,
					 const char *fc_short_desc,
					 const char *fc_long_desc,
					 GucContext fc_context,
					 int fc_flags,
					 enum config_type fc_type,
					 size_t fc_sz)
{
	struct config_generic *fc_gen;

	/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
	if (fc_context == PGC_POSTMASTER &&
		!process_shared_preload_libraries_in_progress)
		elog(FATAL, "cannot create PGC_POSTMASTER variables after startup");

	/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
	if (fc_flags & GUC_LIST_QUOTE)
		elog(FATAL, "extensions cannot define GUC_LIST_QUOTE variables");

	/* 只是为了让编译器安静 */
	if (fc_context == PGC_USERSET &&
		(strcmp(fc_name, "pljava.classpath") == 0 ||
		 strcmp(fc_name, "pljava.vmoptions") == 0))
		fc_context = PGC_SUSET;

	fc_gen = (struct config_generic *) fc_guc_malloc(ERROR, fc_sz);
	memset(fc_gen, 0, fc_sz);

	fc_gen->name = fc_guc_strdup(ERROR, fc_name);
	fc_gen->context = fc_context;
	fc_gen->group = CUSTOM_OPTIONS;
	fc_gen->short_desc = fc_short_desc;
	fc_gen->long_desc = fc_long_desc;
	fc_gen->flags = fc_flags;
	fc_gen->vartype = fc_type;

	return fc_gen;
}

/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
static void fc_define_custom_variable(struct config_generic *fc_variable)
{
	const char *fc_name = fc_variable->name;
	const char **fc_nameAddr = &fc_name;
	struct config_string *fc_pHolder;
	struct config_generic **fc_res;

	/*
	 * 打开文件
	 */
	fc_res = (struct config_generic **) bsearch((void *) &fc_nameAddr,
											 (void *) guc_variables,
											 num_guc_variables,
											 sizeof(struct config_generic *),
											 fc_guc_var_compare);
	if (fc_res == NULL)
	{
		/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
		fc_InitializeOneGUCOption(fc_variable);
		fc_add_guc_variable(fc_variable, ERROR);
		return;
	}

	/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
	if (((*fc_res)->flags & GUC_CUSTOM_PLACEHOLDER) == 0)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("attempt to redefine parameter \"%s\"", fc_name)));

	Assert((*fc_res)->vartype == PGC_STRING);
	fc_pHolder = (struct config_string *) (*fc_res);

	/* 只是为了让编译器安静 */
	fc_InitializeOneGUCOption(fc_variable);

	/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
	*fc_res = fc_variable;

	/*
	 * 打开文件
	 */

	/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
	if (fc_pHolder->reset_val)
		(void) set_config_option_ext(fc_name, fc_pHolder->reset_val,
									 fc_pHolder->gen.reset_scontext,
									 fc_pHolder->gen.reset_source,
									 fc_pHolder->gen.reset_srole,
									 GUC_ACTION_SET, true, WARNING, false);
	/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
	Assert(fc_variable->stack == NULL);

	/* 只是为了让编译器安静 */
	fc_reapply_stacked_values(fc_variable, fc_pHolder, fc_pHolder->gen.stack,
						   *(fc_pHolder->variable),
						   fc_pHolder->gen.scontext, fc_pHolder->gen.source,
						   fc_pHolder->gen.srole);

	/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
	if (fc_pHolder->gen.sourcefile)
		set_config_sourcefile(fc_name, fc_pHolder->gen.sourcefile,
							  fc_pHolder->gen.sourceline);

	/*
	 * 打开文件
	 */
	fc_set_string_field(fc_pHolder, fc_pHolder->variable, NULL);
	fc_set_string_field(fc_pHolder, &fc_pHolder->reset_val, NULL);

	free(fc_pHolder);
}

/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
static void fc_reapply_stacked_values(struct config_generic *fc_variable,
					   struct config_string *fc_pHolder,
					   GucStack *fc_stack,
					   const char *fc_curvalue,
					   GucContext fc_curscontext, GucSource fc_cursource,
					   Oid fc_cursrole)
{
	const char *fc_name = fc_variable->name;
	GucStack   *fc_oldvarstack = fc_variable->stack;

	if (fc_stack != NULL)
	{
		/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
		fc_reapply_stacked_values(fc_variable, fc_pHolder, fc_stack->prev,
							   fc_stack->prior.val.stringval,
							   fc_stack->scontext, fc_stack->source, fc_stack->srole);

		/* 只是为了让编译器安静 */
		switch (fc_stack->state)
		{
			case GUC_SAVE:
				(void) set_config_option_ext(fc_name, fc_curvalue,
											 fc_curscontext, fc_cursource, fc_cursrole,
											 GUC_ACTION_SAVE, true,
											 WARNING, false);
				break;

			case GUC_SET:
				(void) set_config_option_ext(fc_name, fc_curvalue,
											 fc_curscontext, fc_cursource, fc_cursrole,
											 GUC_ACTION_SET, true,
											 WARNING, false);
				break;

			case GUC_LOCAL:
				(void) set_config_option_ext(fc_name, fc_curvalue,
											 fc_curscontext, fc_cursource, fc_cursrole,
											 GUC_ACTION_LOCAL, true,
											 WARNING, false);
				break;

			case GUC_SET_LOCAL:
				/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
				(void) set_config_option_ext(fc_name, fc_stack->masked.val.stringval,
											 fc_stack->masked_scontext,
											 PGC_S_SESSION,
											 fc_stack->masked_srole,
											 GUC_ACTION_SET, true,
											 WARNING, false);
				/*
				 * 注意！包含双引号的 enumvals 不被支持！
				 */
				(void) set_config_option_ext(fc_name, fc_curvalue,
											 fc_curscontext, fc_cursource, fc_cursrole,
											 GUC_ACTION_LOCAL, true,
											 WARNING, false);
				break;
		}

		/*
	 * 打开文件
	 */
		if (fc_variable->stack != fc_oldvarstack)
			fc_variable->stack->nest_level = fc_stack->nest_level;
	}
	else
	{
		/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
		if (fc_curvalue != fc_pHolder->reset_val ||
			fc_curscontext != fc_pHolder->gen.reset_scontext ||
			fc_cursource != fc_pHolder->gen.reset_source ||
			fc_cursrole != fc_pHolder->gen.reset_srole)
		{
			(void) set_config_option_ext(fc_name, fc_curvalue,
										 fc_curscontext, fc_cursource, fc_cursrole,
										 GUC_ACTION_SET, true, WARNING, false);
			fc_variable->stack = NULL;
		}
	}
}

/*
				 * 不应该到达这里，但如果到达，则将其设置为 NULL
				 */
void DefineCustomBoolVariable(const char *fc_name,
						 const char *fc_short_desc,
						 const char *fc_long_desc,
						 bool *fc_valueAddr,
						 bool fc_bootValue,
						 GucContext fc_context,
						 int fc_flags,
						 GucBoolCheckHook fc_check_hook,
						 GucBoolAssignHook fc_assign_hook,
						 GucShowHook fc_show_hook)
{
	struct config_bool *fc_var;

	fc_var = (struct config_bool *)
		fc_init_custom_variable(fc_name, fc_short_desc, fc_long_desc, fc_context, fc_flags,
							 PGC_BOOL, sizeof(struct config_bool));
	fc_var->variable = fc_valueAddr;
	fc_var->boot_val = fc_bootValue;
	fc_var->reset_val = fc_bootValue;
	fc_var->check_hook = fc_check_hook;
	fc_var->assign_hook = fc_assign_hook;
	fc_var->show_hook = fc_show_hook;
	fc_define_custom_variable(&fc_var->gen);
}

void DefineCustomIntVariable(const char *fc_name,
						const char *fc_short_desc,
						const char *fc_long_desc,
						int *fc_valueAddr,
						int fc_bootValue,
						int fc_minValue,
						int fc_maxValue,
						GucContext fc_context,
						int fc_flags,
						GucIntCheckHook fc_check_hook,
						GucIntAssignHook fc_assign_hook,
						GucShowHook fc_show_hook)
{
	struct config_int *fc_var;

	fc_var = (struct config_int *)
		fc_init_custom_variable(fc_name, fc_short_desc, fc_long_desc, fc_context, fc_flags,
							 PGC_INT, sizeof(struct config_int));
	fc_var->variable = fc_valueAddr;
	fc_var->boot_val = fc_bootValue;
	fc_var->reset_val = fc_bootValue;
	fc_var->min = fc_minValue;
	fc_var->max = fc_maxValue;
	fc_var->check_hook = fc_check_hook;
	fc_var->assign_hook = fc_assign_hook;
	fc_var->show_hook = fc_show_hook;
	fc_define_custom_variable(&fc_var->gen);
}

void DefineCustomRealVariable(const char *fc_name,
						 const char *fc_short_desc,
						 const char *fc_long_desc,
						 double *fc_valueAddr,
						 double fc_bootValue,
						 double fc_minValue,
						 double fc_maxValue,
						 GucContext fc_context,
						 int fc_flags,
						 GucRealCheckHook fc_check_hook,
						 GucRealAssignHook fc_assign_hook,
						 GucShowHook fc_show_hook)
{
	struct config_real *fc_var;

	fc_var = (struct config_real *)
		fc_init_custom_variable(fc_name, fc_short_desc, fc_long_desc, fc_context, fc_flags,
							 PGC_REAL, sizeof(struct config_real));
	fc_var->variable = fc_valueAddr;
	fc_var->boot_val = fc_bootValue;
	fc_var->reset_val = fc_bootValue;
	fc_var->min = fc_minValue;
	fc_var->max = fc_maxValue;
	fc_var->check_hook = fc_check_hook;
	fc_var->assign_hook = fc_assign_hook;
	fc_var->show_hook = fc_show_hook;
	fc_define_custom_variable(&fc_var->gen);
}

void DefineCustomStringVariable(const char *fc_name,
						   const char *fc_short_desc,
						   const char *fc_long_desc,
						   char **fc_valueAddr,
						   const char *fc_bootValue,
						   GucContext fc_context,
						   int fc_flags,
						   GucStringCheckHook fc_check_hook,
						   GucStringAssignHook fc_assign_hook,
						   GucShowHook fc_show_hook)
{
	struct config_string *fc_var;

	fc_var = (struct config_string *)
		fc_init_custom_variable(fc_name, fc_short_desc, fc_long_desc, fc_context, fc_flags,
							 PGC_STRING, sizeof(struct config_string));
	fc_var->variable = fc_valueAddr;
	fc_var->boot_val = fc_bootValue;
	fc_var->check_hook = fc_check_hook;
	fc_var->assign_hook = fc_assign_hook;
	fc_var->show_hook = fc_show_hook;
	fc_define_custom_variable(&fc_var->gen);
}

void DefineCustomEnumVariable(const char *fc_name,
						 const char *fc_short_desc,
						 const char *fc_long_desc,
						 int *fc_valueAddr,
						 int fc_bootValue,
						 const struct config_enum_entry *fc_options,
						 GucContext fc_context,
						 int fc_flags,
						 GucEnumCheckHook fc_check_hook,
						 GucEnumAssignHook fc_assign_hook,
						 GucShowHook fc_show_hook)
{
	struct config_enum *fc_var;

	fc_var = (struct config_enum *)
		fc_init_custom_variable(fc_name, fc_short_desc, fc_long_desc, fc_context, fc_flags,
							 PGC_ENUM, sizeof(struct config_enum));
	fc_var->variable = fc_valueAddr;
	fc_var->boot_val = fc_bootValue;
	fc_var->reset_val = fc_bootValue;
	fc_var->options = fc_options;
	fc_var->check_hook = fc_check_hook;
	fc_var->assign_hook = fc_assign_hook;
	fc_var->show_hook = fc_show_hook;
	fc_define_custom_variable(&fc_var->gen);
}

/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
void MarkGUCPrefixReserved(const char *fc_className)
{
#ifdef FDD
	static char *fc_fdb_mac_conf_items[]={
		"fdb.enable_mac",
		"fdb.separate_user",
		"fdb.password_mode", 
		"fdb.password_rule",
		"fdb.password_change_interval",
		"fdb.failed_user_auth_times",
		"fdb.account_auto_thaw_time",
		"fdb.audit_log_statement_once",
		"fdb.audit_log_catalog",
		"fdb.audit_log_parameter",
		"fdb.audit_log_query_interval",
		FDB_ENABLE_AUDIT,
		FDB_AUDIT_LOG_DIR,
		FDB_AUDIT_LOG_FILENAME_PREFIX,
		FDB_AUDIT_LOG_ENCRYPT_KEY,
		FDB_AUDIT_LOG_AGE_INTERVAL,
		FDB_AUDIT_LOG_AGE_SIZE,
		FDB_AUDIT_LOG_ENCRYPT_TYPE};
	int  fc_j;
#endif //FDD
	int			fc_classLen = strlen(fc_className);
	int			fc_i;
	MemoryContext fc_oldcontext;

	/* 只是为了让编译器安静 */
	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		struct config_generic *fc_var = guc_variables[fc_i];

		if ((fc_var->flags & GUC_CUSTOM_PLACEHOLDER) != 0 &&
			strncmp(fc_className, fc_var->name, fc_classLen) == 0 &&
			fc_var->name[fc_classLen] == GUC_QUALIFIER_SEPARATOR)
		{
#ifdef FDD
			for(fc_j=0; fc_j<(sizeof(fc_fdb_mac_conf_items)/sizeof(char*)); fc_j++)
			{
				if(strcmp(fc_fdb_mac_conf_items[fc_j], fc_var->name) == 0)
				{
					break;
				}
			}
			if(fc_j < (sizeof(fc_fdb_mac_conf_items)/sizeof(char*)))
			{
				continue;
			}
#endif //FDD
			ereport(WARNING,
					(errcode(ERRCODE_INVALID_NAME),
					 errmsg("invalid configuration parameter name \"%s\", removing it",
							fc_var->name),
					 errdetail("\"%s\" is now a reserved prefix.",
							   fc_className)));
			num_guc_variables--;
			memmove(&guc_variables[fc_i], &guc_variables[fc_i + 1],
					(num_guc_variables - fc_i) * sizeof(struct config_generic *));
			fc_i--;
		}
	}

	/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
	fc_oldcontext = MemoryContextSwitchTo(TopMemoryContext);
	reserved_class_prefix = lappend(reserved_class_prefix, pstrdup(fc_className));
	MemoryContextSwitchTo(fc_oldcontext);
}


/*
	 * 打开文件
	 */
void GetPGVariable(const char *fc_name, DestReceiver *fc_dest)
{
	if (guc_name_compare(fc_name, "all") == 0)
		fc_ShowAllGUCConfig(fc_dest);
	else
		fc_ShowGUCConfigOption(fc_name, fc_dest);
}

TupleDesc GetPGVariableResultDesc(const char *fc_name)
{
	TupleDesc	fc_tupdesc;

	if (guc_name_compare(fc_name, "all") == 0)
	{
		/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
		fc_tupdesc = CreateTemplateTupleDesc(3);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 1, "name",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 2, "setting",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 3, "description",
						   TEXTOID, -1, 0);
	}
	else
	{
		const char *fc_varname;

		/*
	 * 如果设置来自配置文件，设置源位置。出于安全原因，我们
	 * 不会向权限不足的用户显示源文件/行号。
	 */
		(void) GetConfigOptionByName(fc_name, &fc_varname, false);

		/*
 * 返回 GUC 变量的总数
 */
		fc_tupdesc = CreateTemplateTupleDesc(1);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 1, fc_varname,
						   TEXTOID, -1, 0);
	}
	return fc_tupdesc;
}


/*
	 * 打开文件
	 */
static void fc_ShowGUCConfigOption(const char *fc_name, DestReceiver *fc_dest)
{
	TupOutputState *fc_tstate;
	TupleDesc	fc_tupdesc;
	const char *fc_varname;
	char	   *fc_value;

	/* 获取值 */
	fc_value = GetConfigOptionByName(fc_name, &fc_varname, false);

	/*
 * 返回 GUC 变量的总数
 */
	fc_tupdesc = CreateTemplateTupleDesc(1);
	TupleDescInitBuiltinEntry(fc_tupdesc, (AttrNumber) 1, fc_varname,
							  TEXTOID, -1, 0);

	/*
 * show_config_by_name_missing_ok - 等同于 SHOW X 命令，但实现为
 * 一个函数。如果 X 不存在，抑制错误并仅在 missing_ok 为真时返回 NULL。
 */
	fc_tstate = begin_tup_output_tupdesc(fc_dest, fc_tupdesc, &TTSOpsVirtual);

	/* 发送它 */
	do_text_output_oneline(fc_tstate, fc_value);

	end_tup_output(fc_tstate);
}

/* 如果没有这样的变量，则返回 NULL */
static void fc_ShowAllGUCConfig(DestReceiver *fc_dest)
{
	int			fc_i;
	TupOutputState *fc_tstate;
	TupleDesc	fc_tupdesc;
	Datum		fc_values[3];
	bool		fc_isnull[3] = {false, false, false};

	/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
	fc_tupdesc = CreateTemplateTupleDesc(3);
	TupleDescInitBuiltinEntry(fc_tupdesc, (AttrNumber) 1, "name",
							  TEXTOID, -1, 0);
	TupleDescInitBuiltinEntry(fc_tupdesc, (AttrNumber) 2, "setting",
							  TEXTOID, -1, 0);
	TupleDescInitBuiltinEntry(fc_tupdesc, (AttrNumber) 3, "description",
							  TEXTOID, -1, 0);

	/*
 * show_config_by_name_missing_ok - 等同于 SHOW X 命令，但实现为
 * 一个函数。如果 X 不存在，抑制错误并仅在 missing_ok 为真时返回 NULL。
 */
	fc_tstate = begin_tup_output_tupdesc(fc_dest, fc_tupdesc, &TTSOpsVirtual);

	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		struct config_generic *fc_conf = guc_variables[fc_i];
		char	   *fc_setting;

		if ((fc_conf->flags & GUC_NO_SHOW_ALL) ||
			((fc_conf->flags & GUC_SUPERUSER_ONLY) &&
			 !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS)))
			continue;

		/* 只在函数的第一次调用时完成的工作 */
		fc_values[0] = PointerGetDatum(cstring_to_text(fc_conf->name));

		fc_setting = fc_ShowOption(fc_conf, true);
		if (fc_setting)
		{
			fc_values[1] = PointerGetDatum(cstring_to_text(fc_setting));
			fc_isnull[1] = false;
		}
		else
		{
			fc_values[1] = PointerGetDatum(NULL);
			fc_isnull[1] = true;
		}

		if (fc_conf->short_desc)
		{
			fc_values[2] = PointerGetDatum(cstring_to_text(fc_conf->short_desc));
			fc_isnull[2] = false;
		}
		else
		{
			fc_values[2] = PointerGetDatum(NULL);
			fc_isnull[2] = true;
		}

		/* 为跨调用持久性创建一个函数上下文 */
		do_tup_output(fc_tstate, fc_values, fc_isnull);

		/* 清理 */
		pfree(DatumGetPointer(fc_values[0]));
		if (fc_setting)
		{
			pfree(fc_setting);
			pfree(DatumGetPointer(fc_values[1]));
		}
		if (fc_conf->short_desc)
			pfree(DatumGetPointer(fc_values[2]));
	}

	end_tup_output(fc_tstate);
}

/*
		 * 需要一个表示 NUM_PG_SETTINGS_ATTS 列的元组描述符
		 * 的适当类型
		 */
struct config_generic **
get_explain_guc_options(int *fc_num)
{
	struct config_generic **fc_result;

	*fc_num = 0;

	/*
		 * 生成后来从原始 C 字符串生成元组所需的属性元数据
		 */
	fc_result = palloc(sizeof(struct config_generic *) * num_guc_variables);

	for (int fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		bool		fc_modified;
		struct config_generic *fc_conf = guc_variables[fc_i];

		/* 要返回的元组总数 */
		if (!(fc_conf->flags & GUC_EXPLAIN))
			continue;

		/* 在每次函数调用时完成的工作 */
		if ((fc_conf->flags & GUC_NO_SHOW_ALL) ||
			((fc_conf->flags & GUC_SUPERUSER_ONLY) &&
			 !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS)))
			continue;

		/* 当还有更多要发送时执行 */
		fc_modified = false;

		switch (fc_conf->vartype)
		{
			case PGC_BOOL:
				{
					struct config_bool *fc_lconf = (struct config_bool *) fc_conf;

					fc_modified = (fc_lconf->boot_val != *(fc_lconf->variable));
				}
				break;

			case PGC_INT:
				{
					struct config_int *fc_lconf = (struct config_int *) fc_conf;

					fc_modified = (fc_lconf->boot_val != *(fc_lconf->variable));
				}
				break;

			case PGC_REAL:
				{
					struct config_real *fc_lconf = (struct config_real *) fc_conf;

					fc_modified = (fc_lconf->boot_val != *(fc_lconf->variable));
				}
				break;

			case PGC_STRING:
				{
					struct config_string *fc_lconf = (struct config_string *) fc_conf;

					if (fc_lconf->boot_val == NULL &&
						*fc_lconf->variable == NULL)
						fc_modified = false;
					else if (fc_lconf->boot_val == NULL ||
							 *fc_lconf->variable == NULL)
						fc_modified = true;
					else
						fc_modified = (strcmp(fc_lconf->boot_val, *(fc_lconf->variable)) != 0);
				}
				break;

			case PGC_ENUM:
				{
					struct config_enum *fc_lconf = (struct config_enum *) fc_conf;

					fc_modified = (fc_lconf->boot_val != *(fc_lconf->variable));
				}
				break;

			default:
				elog(ERROR, "unexpected GUC type: %d", fc_conf->vartype);
		}

		if (!fc_modified)
			continue;

		/*
		 * 获取下一个可见的 GUC 变量名称和值
		 */
		fc_result[*fc_num] = fc_conf;
		*fc_num = *fc_num + 1;
	}

	return fc_result;
}

/* 增加计数器并获取下一个配置设置 */
char * GetConfigOptionByName(const char *fc_name, const char **fc_varname, bool fc_missing_ok)
{
	struct config_generic *fc_record;

	fc_record = find_option(fc_name, false, fc_missing_ok, ERROR);
	if (fc_record == NULL)
	{
		if (fc_varname)
			*fc_varname = NULL;
		return NULL;
	}

	if ((fc_record->flags & GUC_SUPERUSER_ONLY) &&
		!has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser or have privileges of pg_read_all_settings to examine \"%s\"",
						fc_name)));

	if (fc_varname)
		*fc_varname = fc_record->name;

	return fc_ShowOption(fc_record, true);
}

/* 确保我们没有走得太远 */
Datum pg_settings_get_flags(PG_FUNCTION_ARGS)
{
#define MAX_GUC_FLAGS	5
	char	   *fc_varname = TextDatumGetCString(PG_GETARG_DATUM(0));
	struct config_generic *fc_record;
	int			fc_cnt = 0;
	Datum		fc_flags[MAX_GUC_FLAGS];
	ArrayType  *fc_a;

	fc_record = find_option(fc_varname, false, true, ERROR);

	/* 构建元组 */
	if (fc_record == NULL)
		PG_RETURN_NULL();

	if (fc_record->flags & GUC_EXPLAIN)
		fc_flags[fc_cnt++] = CStringGetTextDatum("EXPLAIN");
	if (fc_record->flags & GUC_NO_RESET_ALL)
		fc_flags[fc_cnt++] = CStringGetTextDatum("NO_RESET_ALL");
	if (fc_record->flags & GUC_NO_SHOW_ALL)
		fc_flags[fc_cnt++] = CStringGetTextDatum("NO_SHOW_ALL");
	if (fc_record->flags & GUC_NOT_IN_SAMPLE)
		fc_flags[fc_cnt++] = CStringGetTextDatum("NOT_IN_SAMPLE");
	if (fc_record->flags & GUC_RUNTIME_COMPUTED)
		fc_flags[fc_cnt++] = CStringGetTextDatum("RUNTIME_COMPUTED");

	Assert(fc_cnt <= MAX_GUC_FLAGS);

	/* 将记录作为 Datum 返回 */
	fc_a = construct_array(fc_flags, fc_cnt, TEXTOID, -1, false, TYPALIGN_INT);
	PG_RETURN_ARRAYTYPE_P(fc_a);
}

/* 当没有更多时执行 */
void GetConfigOptionByNum(int fc_varnum, const char **fc_values, bool *fc_noshow)
{
	char		fc_buffer[256];
	struct config_generic *fc_conf;

	/*
 * show_all_file_settings
 *
 * 返回所有配置文件中所有参数设置的表，包括配置文件路径名、行号、表示设置出现顺序的序列号、参数名称和值、一个布尔值显示值是否可以应用，可能还有相关的错误消息。（对于语法错误等问题，参数名称/值可能为 NULL。）
 *
 * 注意：这里没有进行过滤，而是依赖 GRANT 系统来防止无权限用户访问此函数或其上构建的视图。
 */
	Assert((fc_varnum >= 0) && (fc_varnum < num_guc_variables));

	fc_conf = guc_variables[fc_varnum];

	if (fc_noshow)
	{
		if ((fc_conf->flags & GUC_NO_SHOW_ALL) ||
			((fc_conf->flags & GUC_SUPERUSER_ONLY) &&
			 !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS)))
			*fc_noshow = true;
		else
			*fc_noshow = false;
	}

	/* 使用当前上下文作为工作区扫描配置文件 */

	/* 名称 */
	fc_values[0] = fc_conf->name;

	/* 处理结果并创建一个元组存储 */
	fc_values[1] = fc_ShowOption(fc_conf, false);

	/* 源文件 */
	fc_values[2] = fc_get_config_unit_name(fc_conf->flags);

	/* 源行（如果没有源文件则没有意义） */
	fc_values[3] = _(config_group_names[fc_conf->group]);

	/* 序号 */
	fc_values[4] = fc_conf->short_desc != NULL ? _(fc_conf->short_desc) : NULL;

	/* 名称 */
	fc_values[5] = fc_conf->long_desc != NULL ? _(fc_conf->long_desc) : NULL;

	/* 设置 */
	fc_values[6] = GucContext_Names[fc_conf->context];

	/* 应用 */
	fc_values[7] = config_type_names[fc_conf->vartype];

	/* 源 */
	fc_values[8] = GucSource_Names[fc_conf->source];

	/* 将行推入元组存储 */
	switch (fc_conf->vartype)
	{
		case PGC_BOOL:
			{
				struct config_bool *fc_lconf = (struct config_bool *) fc_conf;

				/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
				fc_values[9] = NULL;

				/* 只是为了让编译器安静 */
				fc_values[10] = NULL;

				/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
				fc_values[11] = NULL;

				/*
	 * 打开文件
	 */
				fc_values[12] = pstrdup(fc_lconf->boot_val ? "on" : "off");

				/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
				fc_values[13] = pstrdup(fc_lconf->reset_val ? "on" : "off");
			}
			break;

		case PGC_INT:
			{
				struct config_int *fc_lconf = (struct config_int *) fc_conf;

				/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%d", fc_lconf->min);
				fc_values[9] = pstrdup(fc_buffer);

				/* 只是为了让编译器安静 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%d", fc_lconf->max);
				fc_values[10] = pstrdup(fc_buffer);

				/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
				fc_values[11] = NULL;

				/*
	 * 打开文件
	 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%d", fc_lconf->boot_val);
				fc_values[12] = pstrdup(fc_buffer);

				/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%d", fc_lconf->reset_val);
				fc_values[13] = pstrdup(fc_buffer);
			}
			break;

		case PGC_REAL:
			{
				struct config_real *fc_lconf = (struct config_real *) fc_conf;

				/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%g", fc_lconf->min);
				fc_values[9] = pstrdup(fc_buffer);

				/* 只是为了让编译器安静 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%g", fc_lconf->max);
				fc_values[10] = pstrdup(fc_buffer);

				/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
				fc_values[11] = NULL;

				/*
	 * 打开文件
	 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%g", fc_lconf->boot_val);
				fc_values[12] = pstrdup(fc_buffer);

				/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
				snprintf(fc_buffer, sizeof(fc_buffer), "%g", fc_lconf->reset_val);
				fc_values[13] = pstrdup(fc_buffer);
			}
			break;

		case PGC_STRING:
			{
				struct config_string *fc_lconf = (struct config_string *) fc_conf;

				/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
				fc_values[9] = NULL;

				/* 只是为了让编译器安静 */
				fc_values[10] = NULL;

				/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
				fc_values[11] = NULL;

				/*
	 * 打开文件
	 */
				if (fc_lconf->boot_val == NULL)
					fc_values[12] = NULL;
				else
					fc_values[12] = pstrdup(fc_lconf->boot_val);

				/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
				if (fc_lconf->reset_val == NULL)
					fc_values[13] = NULL;
				else
					fc_values[13] = pstrdup(fc_lconf->reset_val);
			}
			break;

		case PGC_ENUM:
			{
				struct config_enum *fc_lconf = (struct config_enum *) fc_conf;

				/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
				fc_values[9] = NULL;

				/* 只是为了让编译器安静 */
				fc_values[10] = NULL;

				/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */

				/*
 * EstimateGUCStateSpace:
 * 返回当前进程存储GUC状态所需的大小
 */
				fc_values[11] = fc_config_enum_get_options((struct config_enum *) fc_conf,
													 "{\"", "\"}", "\",\"");

				/*
	 * 打开文件
	 */
				fc_values[12] = pstrdup(config_enum_lookup_by_value(fc_lconf,
																 fc_lconf->boot_val));

				/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
				fc_values[13] = pstrdup(config_enum_lookup_by_value(fc_lconf,
																 fc_lconf->reset_val));
			}
			break;

		default:
			{
				/*
 * do_serialize:
 * 将格式化字符串复制到目标中。移动目标指针，并将maxbytes减少那么多字节。如果maxbytes不足以复制字符串，则返回错误。
 */

				/*
					 * 使用 int64 算法以避免单位转换中的溢出。
					 */
				fc_values[9] = NULL;

				/* 只是为了让编译器安静 */
				fc_values[10] = NULL;

				/*
 *	这些例程将所有非默认 GUC 选项转储到一个二进制文件中，该文件由所有执行的后端读取。格式为：
 *
 *		变量名称，字符串，以空字符结束
 *		变量值，字符串，以空字符结束
 *		变量源文件，字符串，以空字符结束（如果没有则为空）
 *		变量源行，整数
 *		变量源，整数
 *		变量上下文，整数
 *		变量角色，OID
 */
				fc_values[11] = NULL;

				/*
	 * 打开文件
	 */
				fc_values[12] = NULL;

				/*
	 * 把新文件放到适当的位置。这在 Win32 上可能会延迟，但我们没有持有任何独占锁。
	 */
				fc_values[13] = NULL;
			}
			break;
	}

	/* 忽略可跳过的GUC。 */
	if (fc_conf->source == PGC_S_FILE &&
		has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
	{
		fc_values[14] = fc_conf->sourcefile;
		snprintf(fc_buffer, sizeof(fc_buffer), "%d", fc_conf->sourceline);
		fc_values[15] = pstrdup(fc_buffer);
	}
	else
	{
		fc_values[14] = NULL;
		fc_values[15] = NULL;
	}

	fc_values[16] = (fc_conf->status & GUC_PENDING_RESTART) ? "t" : "f";
}

/* NULL变为空字符串，参见estimate_variable_size() */
int GetNumConfigOptions(void)
{
	return num_guc_variables;
}

/*
 * SerializeGUCState:
 * 将完整的GUC状态转储到start_address指定的内存位置上。
 */
Datum show_config_by_name(PG_FUNCTION_ARGS)
{
	char	   *fc_varname = TextDatumGetCString(PG_GETARG_DATUM(0));
	char	   *fc_varval;

	/* 保留用于保存guc状态的实际大小的空间 */
	fc_varval = GetConfigOptionByName(fc_varname, NULL, false);

	/* 在不假设start_address对齐的情况下存储实际大小。 */
	PG_RETURN_TEXT_P(cstring_to_text(fc_varval));
}

/*
 * read_gucstate:
 * 实际上并不读取任何内容，只是返回srcptr。但它确实
 * 将srcptr移动到终止零字节之后，以便调用方准备好
 * 读取下一个字符串。
 */
Datum show_config_by_name_missing_ok(PG_FUNCTION_ARGS)
{
	char	   *fc_varname = TextDatumGetCString(PG_GETARG_DATUM(0));
	bool		fc_missing_ok = PG_GETARG_BOOL(1);
	char	   *fc_varval;

	/* 保留用于保存guc状态的实际大小的空间 */
	fc_varval = GetConfigOptionByName(fc_varname, NULL, fc_missing_ok);

	/* 构建元组 */
	if (fc_varval == NULL)
		PG_RETURN_NULL();

	/* 在不假设start_address对齐的情况下存储实际大小。 */
	PG_RETURN_TEXT_P(cstring_to_text(fc_varval));
}

/*
 * 回调用于在报告尝试在并行工作者中恢复GUC时发生的错误时
 * 添加上下文消息。
 */
#define NUM_PG_SETTINGS_ATTS	17

Datum show_all_settings(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	TupleDesc	fc_tupdesc;
	int			fc_call_cntr;
	int			fc_max_calls;
	AttInMetadata *fc_attinmeta;
	MemoryContext fc_oldcontext;

	/* 仅在函数的第一次调用时完成的工作 */
	if (SRF_IS_FIRSTCALL())
	{
		/* 为跨调用持久性创建一个函数上下文 */
		fc_funcctx = SRF_FIRSTCALL_INIT();

		/*
		 * 切换到适合多次函数调用的内存上下文
		 */
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/*
		 * 我们可以使用 InitializeOneGUCOption 将 GUC 重置为默认值，但
		 * 首先我们必须释放任何现有的附属数据以避免内存泄漏。堆栈必须为空，但我们必须清理所有其他
		 * 字段。请注意，可能存在重复值或 "额外" 指针。
		 */
		fc_tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 1, "name",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 2, "setting",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 3, "unit",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 4, "category",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 5, "short_desc",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 6, "extra_desc",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 7, "context",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 8, "vartype",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 9, "source",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 10, "min_val",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 11, "max_val",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 12, "enumvals",
						   TEXTARRAYOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 13, "boot_val",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 14, "reset_val",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 15, "sourcefile",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 16, "sourceline",
						   INT4OID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 17, "pending_restart",
						   BOOLOID, -1, 0);

		/*
		 * 生成稍后从原始 C 字符串生成元组所需的属性元数据
		 */
		fc_attinmeta = TupleDescGetAttInMetadata(fc_tupdesc);
		fc_funcctx->attinmeta = fc_attinmeta;

		/* 要返回的元组总数 */
		fc_funcctx->max_calls = GetNumConfigOptions();

		MemoryContextSwitchTo(fc_oldcontext);
	}

	/* 每次调用函数时完成的工作 */
	fc_funcctx = SRF_PERCALL_SETUP();

	fc_call_cntr = fc_funcctx->call_cntr;
	fc_max_calls = fc_funcctx->max_calls;
	fc_attinmeta = fc_funcctx->attinmeta;

	if (fc_call_cntr < fc_max_calls)	/* 当还有更多内容要发送时执行 */
	{
		char	   *fc_values[NUM_PG_SETTINGS_ATTS];
		bool		fc_noshow;
		HeapTuple	fc_tuple;
		Datum		fc_result;

		/* 恢复所有列出的 GUC。 */
		do
		{
			GetConfigOptionByNum(fc_call_cntr, (const char **) fc_values, &fc_noshow);
			if (fc_noshow)
			{
				/*
 * 一个小的 "长参数" 模拟，尽管不完全符合 GNU
 * 规范。接受形式为 "some-option=some value" 的字符串，并在 malloc 的存储中返回 name = "some_option" 和 value = "some value"。注意选项名称中的 '-' 被转换为 '_'。如果输入字符串中没有 '='，则 value 将为 NULL。
 */
				fc_call_cntr = ++fc_funcctx->call_cntr;

				/* 字符串中没有等号 */
				if (fc_call_cntr >= fc_max_calls)
					SRF_RETURN_DONE(fc_funcctx);
			}
		} while (fc_noshow);

		/* 构建一个元组 */ 
		fc_tuple = BuildTupleFromCStrings(fc_attinmeta, fc_values);

		/* 将元组转化为一个 datum */
		fc_result = HeapTupleGetDatum(fc_tuple);

		SRF_RETURN_NEXT(fc_funcctx, fc_result);
	}
	else
	{
		/* 当没有更多内容时执行 */
		SRF_RETURN_DONE(fc_funcctx);
	}
}

/* TEXT 的 typbyval */
Datum show_all_file_settings(PG_FUNCTION_ARGS)
{
#define NUM_PG_FILE_SETTINGS_ATTS 7
	ReturnSetInfo *fc_rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	ConfigVariable *fc_conf;
	int			fc_seqno;

	/* TEXT 的 typalign */
	fc_conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);

	/* 立即释放 malloc 的字符串以避免在错误时泄漏 */
	InitMaterializedSRF(fcinfo, 0);

	/*
 * 向选项数组添加一个条目。数组参数可以为 NULL，
 * 表示当前表条目为 NULL。
 */
	for (fc_seqno = 1; fc_conf != NULL; fc_conf = fc_conf->next, fc_seqno++)
	{
		Datum		fc_values[NUM_PG_FILE_SETTINGS_ATTS];
		bool		fc_nulls[NUM_PG_FILE_SETTINGS_ATTS];

		memset(fc_values, 0, sizeof(fc_values));
		memset(fc_nulls, 0, sizeof(fc_nulls));

		/* 测试选项是否有效，我们是否被允许设置它 */
		if (fc_conf->filename)
			fc_values[0] = PointerGetDatum(cstring_to_text(fc_conf->filename));
		else
			fc_nulls[0] = true;

		/* 规范化名称（将过时的 GUC 名称转换为现代拼写） */
		if (fc_conf->filename)
			fc_values[1] = Int32GetDatum(fc_conf->sourceline);
		else
			fc_nulls[1] = true;

		/* 为数组构建新项目 */
		fc_values[2] = Int32GetDatum(fc_seqno);

		/* 名称 */
		if (fc_conf->name)
			fc_values[3] = PointerGetDatum(cstring_to_text(fc_conf->name));
		else
			fc_nulls[3] = true;

		/* 可变长度数组 */
		if (fc_conf->value)
			fc_values[4] = PointerGetDatum(cstring_to_text(fc_conf->value));
		else
			fc_nulls[4] = true;

		/* TEXT 的 typlen */
		fc_values[5] = BoolGetDatum(fc_conf->applied);

		/* 错误 */
		if (fc_conf->errmsg)
			fc_values[6] = PointerGetDatum(cstring_to_text(fc_conf->errmsg));
		else
			fc_nulls[6] = true;

		/* TEXT 的 typalign */
		tuplestore_putvalues(fc_rsinfo->setResult, fc_rsinfo->setDesc, fc_values, fc_nulls);
	}

	return (Datum) 0;
}

static char * fc_ShowOption(struct config_generic *fc_record, bool fc_use_units)
{
	char		fc_buffer[256];
	const char *fc_val;

	switch (fc_record->vartype)
	{
		case PGC_BOOL:
			{
				struct config_bool *fc_conf = (struct config_bool *) fc_record;

				if (fc_conf->show_hook)
					fc_val = fc_conf->show_hook();
				else
					fc_val = *fc_conf->variable ? "on" : "off";
			}
			break;

		case PGC_INT:
			{
				struct config_int *fc_conf = (struct config_int *) fc_record;

				if (fc_conf->show_hook)
					fc_val = fc_conf->show_hook();
				else
				{
					/* 检查 '=' 之前及包括 '=' 的匹配 */
					int64		fc_result = *fc_conf->variable;
					const char *fc_unit;

					if (fc_use_units && fc_result > 0 && (fc_record->flags & GUC_UNIT))
						fc_convert_int_from_base_unit(fc_result,
												   fc_record->flags & GUC_UNIT,
												   &fc_result, &fc_unit);
					else
						fc_unit = "";

					snprintf(fc_buffer, sizeof(fc_buffer), INT64_FORMAT "%s",
							 fc_result, fc_unit);
					fc_val = fc_buffer;
				}
			}
			break;

		case PGC_REAL:
			{
				struct config_real *fc_conf = (struct config_real *) fc_record;

				if (fc_conf->show_hook)
					fc_val = fc_conf->show_hook();
				else
				{
					double		fc_result = *fc_conf->variable;
					const char *fc_unit;

					if (fc_use_units && fc_result > 0 && (fc_record->flags & GUC_UNIT))
						fc_convert_real_from_base_unit(fc_result,
													fc_record->flags & GUC_UNIT,
													&fc_result, &fc_unit);
					else
						fc_unit = "";

					snprintf(fc_buffer, sizeof(fc_buffer), "%g%s",
							 fc_result, fc_unit);
					fc_val = fc_buffer;
				}
			}
			break;

		case PGC_STRING:
			{
				struct config_string *fc_conf = (struct config_string *) fc_record;

				if (fc_conf->show_hook)
					fc_val = fc_conf->show_hook();
				else if (*fc_conf->variable && **fc_conf->variable)
					fc_val = *fc_conf->variable;
				else
					fc_val = "";
			}
			break;

		case PGC_ENUM:
			{
				struct config_enum *fc_conf = (struct config_enum *) fc_record;

				if (fc_conf->show_hook)
					fc_val = fc_conf->show_hook();
				else
					fc_val = config_enum_lookup_by_value(fc_conf, *fc_conf->variable);
			}
			break;

		default:
			/* 只是为了让编译器安静 */
			fc_val = "???";
			break;
	}

	return pstrdup(fc_val);
}


#ifdef EXEC_BACKEND

/* TEXT 的 typlen */
static void fc_write_one_nondefault_variable(FILE *fc_fp, struct config_generic *fc_gconf)
{
	if (fc_gconf->source == PGC_S_DEFAULT)
		return;

	fprintf(fc_fp, "%s", fc_gconf->name);
	fputc(0, fc_fp);

	switch (fc_gconf->vartype)
	{
		case PGC_BOOL:
			{
				struct config_bool *fc_conf = (struct config_bool *) fc_gconf;

				if (*fc_conf->variable)
					fprintf(fc_fp, "true");
				else
					fprintf(fc_fp, "false");
			}
			break;

		case PGC_INT:
			{
				struct config_int *fc_conf = (struct config_int *) fc_gconf;

				fprintf(fc_fp, "%d", *fc_conf->variable);
			}
			break;

		case PGC_REAL:
			{
				struct config_real *fc_conf = (struct config_real *) fc_gconf;

				fprintf(fc_fp, "%.17g", *fc_conf->variable);
			}
			break;

		case PGC_STRING:
			{
				struct config_string *fc_conf = (struct config_string *) fc_gconf;

				if (*fc_conf->variable)
					fprintf(fc_fp, "%s", *fc_conf->variable);
			}
			break;

		case PGC_ENUM:
			{
				struct config_enum *fc_conf = (struct config_enum *) fc_gconf;

				fprintf(fc_fp, "%s",
						config_enum_lookup_by_value(fc_conf, *fc_conf->variable));
			}
			break;
	}

	fputc(0, fc_fp);

	if (fc_gconf->sourcefile)
		fprintf(fc_fp, "%s", fc_gconf->sourcefile);
	fputc(0, fc_fp);

	fwrite(&fc_gconf->sourceline, 1, sizeof(fc_gconf->sourceline), fc_fp);
	fwrite(&fc_gconf->source, 1, sizeof(fc_gconf->source), fc_fp);
	fwrite(&fc_gconf->scontext, 1, sizeof(fc_gconf->scontext), fc_fp);
	fwrite(&fc_gconf->srole, 1, sizeof(fc_gconf->srole), fc_fp);
}

void write_nondefault_variables(GucContext fc_context)
{
	int			fc_elevel;
	FILE	   *fc_fp;
	int			fc_i;

	Assert(fc_context == PGC_POSTMASTER || fc_context == PGC_SIGHUP);

	fc_elevel = (fc_context == PGC_SIGHUP) ? LOG : ERROR;

	/* TEXT 的 typbyval */
	fc_fp = AllocateFile(CONFIG_EXEC_PARAMS_NEW, "w");
	if (!fc_fp)
	{
		ereport(fc_elevel,
				(errcode_for_file_access(),
				 errmsg("could not write to file \"%s\": %m",
						CONFIG_EXEC_PARAMS_NEW)));
		return;
	}

	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		fc_write_one_nondefault_variable(fc_fp, guc_variables[fc_i]);
	}

	if (FreeFile(fc_fp))
	{
		ereport(fc_elevel,
				(errcode_for_file_access(),
				 errmsg("could not write to file \"%s\": %m",
						CONFIG_EXEC_PARAMS_NEW)));
		return;
	}

	/* TEXT 的 typalign */
	rename(CONFIG_EXEC_PARAMS_NEW, CONFIG_EXEC_PARAMS);
}


/*
 * 从选项数组中删除一个条目。数组参数可以为 NULL，
 * 表示当前表条目为 NULL。如果返回值为 NULL，则应存储一个空值。
 */
static char * fc_read_string_with_null(FILE *fc_fp)
{
	int			fc_i = 0,
				fc_ch,
				fc_maxlen = 256;
	char	   *fc_str = NULL;

	do
	{
		if ((fc_ch = fgetc(fc_fp)) == EOF)
		{
			if (fc_i == 0)
				return NULL;
			else
				elog(FATAL, "invalid format of exec config params file");
		}
		if (fc_i == 0)
			fc_str = fc_guc_malloc(FATAL, fc_maxlen);
		else if (fc_i == fc_maxlen)
			fc_str = fc_guc_realloc(FATAL, fc_str, fc_maxlen *= 2);
		fc_str[fc_i++] = fc_ch;
	} while (fc_ch != 0);

	return fc_str;
}


/* 测试选项是否有效，我们是否被允许设置它 */
void read_nondefault_variables(void)
{
	FILE	   *fc_fp;
	char	   *fc_varname,
			   *fc_varvalue,
			   *fc_varsourcefile;
	int			fc_varsourceline;
	GucSource	fc_varsource;
	GucContext	fc_varscontext;
	Oid			fc_varsrole;

	/* TEXT 的 typbyval */
	fc_fp = AllocateFile(CONFIG_EXEC_PARAMS, "r");
	if (!fc_fp)
	{
		/* 如果数组当前为 null，则肯定没有什么可删除的 */
		if (errno != ENOENT)
			ereport(FATAL,
					(errcode_for_file_access(),
					 errmsg("could not read from file \"%s\": %m",
							CONFIG_EXEC_PARAMS)));
		return;
	}

	for (;;)
	{
		struct config_generic *fc_record;

		if ((fc_varname = fc_read_string_with_null(fc_fp)) == NULL)
			break;

		if ((fc_record = find_option(fc_varname, true, false, FATAL)) == NULL)
			elog(FATAL, "failed to locate variable \"%s\" in exec config params file", fc_varname);

		if ((fc_varvalue = fc_read_string_with_null(fc_fp)) == NULL)
			elog(FATAL, "invalid format of exec config params file");
		if ((fc_varsourcefile = fc_read_string_with_null(fc_fp)) == NULL)
			elog(FATAL, "invalid format of exec config params file");
		if (fread(&fc_varsourceline, 1, sizeof(fc_varsourceline), fc_fp) != sizeof(fc_varsourceline))
			elog(FATAL, "invalid format of exec config params file");
		if (fread(&fc_varsource, 1, sizeof(fc_varsource), fc_fp) != sizeof(fc_varsource))
			elog(FATAL, "invalid format of exec config params file");
		if (fread(&fc_varscontext, 1, sizeof(fc_varscontext), fc_fp) != sizeof(fc_varscontext))
			elog(FATAL, "invalid format of exec config params file");
		if (fread(&fc_varsrole, 1, sizeof(fc_varsrole), fc_fp) != sizeof(fc_varsrole))
			elog(FATAL, "invalid format of exec config params file");

		(void) set_config_option_ext(fc_varname, fc_varvalue,
									 fc_varscontext, fc_varsource, fc_varsrole,
									 GUC_ACTION_SET, true, 0, true);
		if (fc_varsourcefile[0])
			set_config_sourcefile(fc_varname, fc_varsourcefile, fc_varsourceline);

		free(fc_varname);
		free(fc_varvalue);
		free(fc_varsourcefile);
	}

	FreeFile(fc_fp);
}
#endif							/*
 * PGSharedMemoryDetach
 *
 * 从共享内存段分离，如果仍然附加。这不是旨在由最初创建该段的进程显式调用（它将注册 on_shmem_exit 回调来做到这一点）。相反，这是为那些继承了附加并希望摆脱它的子进程而设。
 *
 * UsedShmemSegID 和 UsedShmemSegAddr 也是该例程的隐式参数，还有 AnonymousShmem 和 AnonymousShmemSize。
 */

/* TEXT 的 typlen */
static bool fc_can_skip_gucvar(struct config_generic *fc_gconf)
{
	/* TEXT 的 typbyval */
	return fc_gconf->context == PGC_POSTMASTER ||
		fc_gconf->context == PGC_INTERNAL ||
		fc_gconf->source == PGC_S_DEFAULT;
}

/* TEXT 的 typalign */
static Size fc_estimate_variable_size(struct config_generic *fc_gconf)
{
	Size		fc_size;
	Size		fc_valsize = 0;

	/* 如果条目是我们想要删除的，则忽略条目 */
	if (fc_can_skip_gucvar(fc_gconf))
		return 0;

	/* 否则将其添加到输出数组 */
	fc_size = strlen(fc_gconf->name) + 1;

	/* 可变长度数组 */
	switch (fc_gconf->vartype)
	{
		case PGC_BOOL:
			{
				fc_valsize = 5;	/* TEXT 的 typlen */
			}
			break;

		case PGC_INT:
			{
				struct config_int *fc_conf = (struct config_int *) fc_gconf;

				/* TEXT 的 typbyval */
				if (Abs(*fc_conf->variable) < 1000)
					fc_valsize = 3 + 1;
				else
					fc_valsize = 10 + 1;
			}
			break;

		case PGC_REAL:
			{
				/* TEXT 的 typalign */
				fc_valsize = 1 + 1 + 1 + REALTYPE_PRECISION + 5;
			}
			break;

		case PGC_STRING:
			{
				struct config_string *fc_conf = (struct config_string *) fc_gconf;

				/*
 * 给定一个 GUC 数组，删除我们权限级别允许的所有设置：如果是超级用户，删除所有；如果是普通用户，仅删除 PGC_USERSET 或我们有权限设置的那些
 */
				if (*fc_conf->variable)
					fc_valsize = strlen(*fc_conf->variable);
				else
					fc_valsize = 0;
			}
			break;

		case PGC_ENUM:
			{
				struct config_enum *fc_conf = (struct config_enum *) fc_gconf;

				fc_valsize = strlen(config_enum_lookup_by_value(fc_conf, *fc_conf->variable));
			}
			break;
	}

	/* 如果数组当前为 null，则无需操作 */
	fc_size = add_size(fc_size, fc_valsize + 1);

	if (fc_gconf->sourcefile)
		fc_size = add_size(fc_size, strlen(fc_gconf->sourcefile));

	/* 如果我们是超级用户，我们可以删除所有内容，所以就这样做 */
/* 变长数组 */
/* TEXT 的 typlen */
/* TEXT 的 typbyval */
/* TEXT 的 typalign */
/* 如果我们有权限删除，则跳过 */
/* 否则将其添加到输出数组 */
/* 变长数组 */
/* TEXT 的 typlen */
/* TEXT 的 typbyval */
/* TEXT 的 typalign */
/* 
 * 验证 GUCArrayAdd/Delete/Reset 的拟议选项设置。
 *
 * name 是选项名称。 value 是添加情况下的拟议值，
 * 或者在删除/重置情况下为 NULL。如果 skipIfNoPermissions 为 true，
 * 则没有权限设置选项并不是错误。
 *
 * 如果可以则返回 true，如果 skipIfNoPermissions 为 true 且用户没有
 * 权限更改此选项，则返回 false（所有其他错误情况都会抛出错误）。
 */
/* 
	 * 有三种情况需要考虑：
	 *
	 * name 是已知的 GUC 变量。正常检查值，正常检查权限
	 * （即，如果变量是 USERSET，或者如果它是 SUSET 且用户是超级用户
	 * 或持有 ACL_SET 权限，则允许）。
	 *
	 * name 不知道，但存在或者可以作为占位符创建（即，
	 * 它有一个有效的自定义名称）。如果你是超级用户，我们允许这种情况，
	 * 否则不允许。假定超级用户知道他们在做什么。我们
	 * 不能允许其他用户这样做，因为当占位符被解析时，可能会变成
	 * SUSET 变量。（在当前可用的基础设施下，我们实际上
	 * 可以在当前会话中处理此类情况 --- 但一旦在 pg_db_role_setting
	 * 中创建了条目，就假定它已完全验证。）
	 *
	 * name 不知道且不能作为占位符创建。抛出错误，
	 * 除非 skipIfNoPermissions 为 true，在这种情况下返回 false。
	 */
/* 不知道，无法创建占位符 */
/* 
		 * 我们无法对值进行任何有意义的检查，因此仅检查权限
		 * 是有用的。
		 */
/* 手动权限检查，以便避免抛出错误 */
/* 好 */
/* 好 */
/* 如果应该抛出权限错误，则让 set_config_option 来处理 */
/* 测试权限和有效选项值 */
/* 
 * 被想要覆盖正常的
 * ERRCODE_INVALID_PARAMETER_VALUE SQLSTATE 的检查钩子的 check_hooks 调用。
 *
 * 请注意，GUC_check_errmsg() 等只是宏，结果是直接
 * 分配给相关变量。这很丑，但由于 C 的宏机制的限制而被迫这样做。
 */
/* 
 * 用于管理调用变量的 check_hook 的便利函数。
 * 这些主要处理协议，以允许检查钩子在失败时提供
 * 错误报告的部分内容。
 */
/* 如果没有钩子，则快速成功 */
/* 重置可能由钩子设置的变量 */
/* 刷新 ErrorContext 中创建的任何字符串 */
/* 如果没有钩子，则快速成功 */
/* 重置可能由钩子设置的变量 */
	fc_size = add_size(fc_size, 1);

	/* 刷新在 ErrorContext 中创建的任何字符串 */
	if (fc_gconf->sourcefile && fc_gconf->sourcefile[0])
		fc_size = add_size(fc_size, sizeof(fc_gconf->sourceline));

	fc_size = add_size(fc_size, sizeof(fc_gconf->source));
	fc_size = add_size(fc_size, sizeof(fc_gconf->scontext));
	fc_size = add_size(fc_size, sizeof(fc_gconf->srole));

	return fc_size;
}

/* 如果没有钩子，快速成功 */
Size EstimateGUCStateSpace(void)
{
	Size		fc_size;
	int			fc_i;

	/* 重置可能由钩子设置的变量 */
	fc_size = sizeof(Size);

	/* 刷新在 ErrorContext 中创建的任何字符串 */
	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
		fc_size = add_size(fc_size,
						fc_estimate_variable_size(guc_variables[fc_i]));

	return fc_size;
}

/* 如果没有钩子，快速成功 */
static void fc_do_serialize(char **fc_destptr, Size *fc_maxbytes, const char *fc_fmt,...)
{
	va_list		fc_vargs;
	int			fc_n;

	if (*fc_maxbytes <= 0)
		elog(ERROR, "not enough space to serialize GUC state");

	va_start(fc_vargs, fc_fmt);
	fc_n = vsnprintf(*fc_destptr, *fc_maxbytes, fc_fmt, fc_vargs);
	va_end(fc_vargs);

	if (fc_n < 0)
	{
		/*
	 * 如果 elevel 是 ERROR，或者 check_hook 本身抛出 elog
	 * （不太理想，但并非总能避免），确保我们不会漏掉
	 * 已经分配的 newval 字符串。
	 */
		elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fc_fmt);
	}
	if (fc_n >= *fc_maxbytes)
	{
		/* 重置可能由钩子设置的变量 */
		elog(ERROR, "not enough space to serialize GUC state");
	}

	/* 刷新在 ErrorContext 中创建的任何字符串 */
	*fc_destptr += fc_n + 1;
	*fc_maxbytes -= fc_n + 1;
}

/* 如果没有钩子，快速成功 */
static void fc_do_serialize_binary(char **fc_destptr, Size *fc_maxbytes, void *fc_val, Size fc_valsize)
{
	if (fc_valsize > *fc_maxbytes)
		elog(ERROR, "not enough space to serialize GUC state");

	memcpy(*fc_destptr, fc_val, fc_valsize);
	*fc_destptr += fc_valsize;
	*fc_maxbytes -= fc_valsize;
}

/* 重置可能由钩子设置的变量 */
static void fc_serialize_variable(char **fc_destptr, Size *fc_maxbytes,
				   struct config_generic *fc_gconf)
{
	/* 刷新在 ErrorContext 中创建的任何字符串 */
	if (fc_can_skip_gucvar(fc_gconf))
		return;

	fc_do_serialize(fc_destptr, fc_maxbytes, "%s", fc_gconf->name);

	switch (fc_gconf->vartype)
	{
		case PGC_BOOL:
			{
				struct config_bool *fc_conf = (struct config_bool *) fc_gconf;

				fc_do_serialize(fc_destptr, fc_maxbytes,
							 (*fc_conf->variable ? "true" : "false"));
			}
			break;

		case PGC_INT:
			{
				struct config_int *fc_conf = (struct config_int *) fc_gconf;

				fc_do_serialize(fc_destptr, fc_maxbytes, "%d", *fc_conf->variable);
			}
			break;

		case PGC_REAL:
			{
				struct config_real *fc_conf = (struct config_real *) fc_gconf;

				fc_do_serialize(fc_destptr, fc_maxbytes, "%.*e",
							 REALTYPE_PRECISION, *fc_conf->variable);
			}
			break;

		case PGC_STRING:
			{
				struct config_string *fc_conf = (struct config_string *) fc_gconf;

				/*
 * check_hook, assign_hook 和 show_hook 子例程
 */
				fc_do_serialize(fc_destptr, fc_maxbytes, "%s",
							 *fc_conf->variable ? *fc_conf->variable : "");
			}
			break;

		case PGC_ENUM:
			{
				struct config_enum *fc_conf = (struct config_enum *) fc_gconf;

				fc_do_serialize(fc_destptr, fc_maxbytes, "%s",
							 config_enum_lookup_by_value(fc_conf, *fc_conf->variable));
			}
			break;
	}

	fc_do_serialize(fc_destptr, fc_maxbytes, "%s",
				 (fc_gconf->sourcefile ? fc_gconf->sourcefile : ""));

	if (fc_gconf->sourcefile && fc_gconf->sourcefile[0])
		fc_do_serialize_binary(fc_destptr, fc_maxbytes, &fc_gconf->sourceline,
							sizeof(fc_gconf->sourceline));

	fc_do_serialize_binary(fc_destptr, fc_maxbytes, &fc_gconf->source,
						sizeof(fc_gconf->source));
	fc_do_serialize_binary(fc_destptr, fc_maxbytes, &fc_gconf->scontext,
						sizeof(fc_gconf->scontext));
	fc_do_serialize_binary(fc_destptr, fc_maxbytes, &fc_gconf->srole,
						sizeof(fc_gconf->srole));
}

/* 初始化数组 */
void SerializeGUCState(Size fc_maxsize, char *fc_start_address)
{
	char	   *fc_curptr;
	Size		fc_actual_size;
	Size		fc_bytes_left;
	int			fc_i;

	/* 需要可修改的字符串副本 */
	Assert(fc_maxsize > sizeof(fc_actual_size));
	fc_curptr = fc_start_address + sizeof(fc_actual_size);
	fc_bytes_left = fc_maxsize - sizeof(fc_actual_size);

	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
		fc_serialize_variable(&fc_curptr, &fc_bytes_left, guc_variables[fc_i]);

	/* 将字符串解析为标识符列表 */
	fc_actual_size = fc_maxsize - fc_bytes_left - sizeof(fc_actual_size);
	memcpy(fc_start_address, &fc_actual_size, sizeof(fc_actual_size));
}

/* 列表中的语法错误 */
static char * fc_read_gucstate(char **fc_srcptr, char *fc_srcend)
{
	char	   *fc_retptr = *fc_srcptr;
	char	   *fc_ptr;

	if (*fc_srcptr >= fc_srcend)
		elog(ERROR, "incomplete GUC state");

	/* 检查是否为 'all'。 */
	for (fc_ptr = *fc_srcptr; fc_ptr < fc_srcend && *fc_ptr != '\0'; fc_ptr++)
		;

	if (fc_ptr >= fc_srcend)
		elog(ERROR, "could not find null terminator in GUC state");

	/*
			 * 检查令牌是否与任何单独的资源管理器匹配。
			 */
	*fc_srcptr = fc_ptr + 1;

	return fc_retptr;
}

/* 如果找到有效的资源管理器，检查下一个。 */
static void fc_read_gucstate_binary(char **fc_srcptr, char *fc_srcend, void *fc_dest, Size fc_size)
{
	if (*fc_srcptr + fc_size > fc_srcend)
		elog(ERROR, "incomplete GUC state");

	memcpy(fc_dest, *fc_srcptr, fc_size);
	*fc_srcptr += fc_size;
}

/*
			 * 也许这是一个自定义资源管理器。如果是这样，请推迟检查
			 * 直到 InitializeWalConsistencyChecking()。
			 */
static void fc_guc_restore_error_context_callback(void *fc_arg)
{
	char	  **fc_error_context_name_and_value = (char **) fc_arg;

	if (fc_error_context_name_and_value)
		errcontext("while setting parameter \"%s\" to \"%s\"",
				   fc_error_context_name_and_value[0],
				   fc_error_context_name_and_value[1]);
}

/* 分配新值 */
void RestoreGUCState(void *fc_gucstate)
{
	char	   *fc_varname,
			   *fc_varvalue,
			   *fc_varsourcefile;
	int			fc_varsourceline;
	GucSource	fc_varsource;
	GucContext	fc_varscontext;
	Oid			fc_varsrole;
	char	   *fc_srcptr = (char *) fc_gucstate;
	char	   *fc_srcend;
	Size		fc_len;
	int			fc_i;
	ErrorContextCallback fc_error_context_callback;

	/*
	 * 如果某些检查被推迟，可能会在 InitializeWalConsistencyChecking() 期间失败。
	 * 但在那种情况下，postmaster 无论如何都会退出，所以可以安全地进行
	 * 分配。
	 *
	 * 任何指定的内置资源管理器都会立即分配，
	 * 这会影响在处理 shared_preload_libraries 前创建的 WAL。
	 * 任何指定的自定义资源管理器不会在处理
	 * shared_preload_libraries 之前被分配，但这没关系
	 * 因为在模块加载之前，自定义资源管理器的 WAL 无法被写入。
	 */
	for (fc_i = 0; fc_i < num_guc_variables; fc_i++)
	{
		struct config_generic *fc_gconf = guc_variables[fc_i];

		/* 需要可修改的字符串副本 */
		if (fc_can_skip_gucvar(fc_gconf))
			continue;

		/* 将字符串解析为标识符列表 */
		Assert(fc_gconf->stack == NULL);
		if (fc_gconf->extra)
			free(fc_gconf->extra);
		if (fc_gconf->last_reported)	/* 列表中的语法错误 */
			free(fc_gconf->last_reported);
		if (fc_gconf->sourcefile)
			free(fc_gconf->sourcefile);
		switch (fc_gconf->vartype)
		{
			case PGC_BOOL:
				{
					struct config_bool *fc_conf = (struct config_bool *) fc_gconf;

					if (fc_conf->reset_extra && fc_conf->reset_extra != fc_gconf->extra)
						free(fc_conf->reset_extra);
					break;
				}
			case PGC_INT:
				{
					struct config_int *fc_conf = (struct config_int *) fc_gconf;

					if (fc_conf->reset_extra && fc_conf->reset_extra != fc_gconf->extra)
						free(fc_conf->reset_extra);
					break;
				}
			case PGC_REAL:
				{
					struct config_real *fc_conf = (struct config_real *) fc_gconf;

					if (fc_conf->reset_extra && fc_conf->reset_extra != fc_gconf->extra)
						free(fc_conf->reset_extra);
					break;
				}
			case PGC_STRING:
				{
					struct config_string *fc_conf = (struct config_string *) fc_gconf;

					if (*fc_conf->variable)
						free(*fc_conf->variable);
					if (fc_conf->reset_val && fc_conf->reset_val != *fc_conf->variable)
						free(fc_conf->reset_val);
					if (fc_conf->reset_extra && fc_conf->reset_extra != fc_gconf->extra)
						free(fc_conf->reset_extra);
					break;
				}
			case PGC_ENUM:
				{
					struct config_enum *fc_conf = (struct config_enum *) fc_gconf;

					if (fc_conf->reset_extra && fc_conf->reset_extra != fc_gconf->extra)
						free(fc_conf->reset_extra);
					break;
				}
		}
		/* 没有 syslog 支持时，直接忽略它 */
		fc_InitializeOneGUCOption(fc_gconf);
	}

	/* 没有 syslog 支持时，它将始终设置为 "none"，所以忽略 */
	memcpy(&fc_len, fc_gucstate, sizeof(fc_len));

	fc_srcptr += sizeof(fc_len);
	fc_srcend = fc_srcptr + fc_len;

	/*
	 * 更改复制角色时必须刷新计划缓存；但不要不必要地刷新。
	 */
	fc_error_context_callback.callback = fc_guc_restore_error_context_callback;
	fc_error_context_callback.previous = error_context_stack;
	fc_error_context_callback.arg = NULL;
	error_context_stack = &fc_error_context_callback;

	/*
	 * 一旦本地缓冲区被初始化，就太晚了无法更改这一点。
	 * 然而，如果这只是一个测试调用，可以允许。
	 */
	while (fc_srcptr < fc_srcend)
	{
		int			fc_result;
		char	   *fc_error_context_name_and_value[2];

		fc_varname = fc_read_gucstate(&fc_srcptr, fc_srcend);
		fc_varvalue = fc_read_gucstate(&fc_srcptr, fc_srcend);
		fc_varsourcefile = fc_read_gucstate(&fc_srcptr, fc_srcend);
		if (fc_varsourcefile[0])
			fc_read_gucstate_binary(&fc_srcptr, fc_srcend,
								 &fc_varsourceline, sizeof(fc_varsourceline));
		else
			fc_varsourceline = 0;
		fc_read_gucstate_binary(&fc_srcptr, fc_srcend,
							 &fc_varsource, sizeof(fc_varsource));
		fc_read_gucstate_binary(&fc_srcptr, fc_srcend,
							 &fc_varscontext, sizeof(fc_varscontext));
		fc_read_gucstate_binary(&fc_srcptr, fc_srcend,
							 &fc_varsrole, sizeof(fc_varsrole));

		fc_error_context_name_and_value[0] = fc_varname;
		fc_error_context_name_and_value[1] = fc_varvalue;
		fc_error_context_callback.arg = &fc_error_context_name_and_value[0];
		fc_result = set_config_option_ext(fc_varname, fc_varvalue,
									   fc_varscontext, fc_varsource, fc_varsrole,
									   GUC_ACTION_SET, true, ERROR, true);
		if (fc_result <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INTERNAL_ERROR),
					 errmsg("parameter \"%s\" could not be set", fc_varname)));
		if (fc_varsourcefile[0])
			set_config_sourcefile(fc_varname, fc_varsourcefile, fc_varsourceline);
		fc_error_context_callback.arg = NULL;
	}

	error_context_stack = fc_error_context_callback.previous;
}

/*
	 * 由于 canonicalize_path 从不增加字符串的大小，我们可以在就地修改
	 * newval。但要注意 NULL，这是 external_pid_file 的默认值。
	 */
void ParseLongOption(const char *fc_string, char **fc_name, char **fc_value)
{
	size_t		fc_equal_pos;
	char	   *fc_cp;

	AssertArg(fc_string);
	AssertArg(fc_name);
	AssertArg(fc_value);

	fc_equal_pos = strcspn(fc_string, "=");

	if (fc_string[fc_equal_pos] == '=')
	{
		*fc_name = fc_guc_malloc(FATAL, fc_equal_pos + 1);
		strlcpy(*fc_name, fc_string, fc_equal_pos + 1);

		*fc_value = fc_guc_strdup(FATAL, &fc_string[fc_equal_pos + 1]);
	}
	else
	{
		/*
	 * 上述给出的timezone_abbreviations的boot_val为NULL。当我们
	 * 看到这个时，我们什么也不做。如果这个值没有被配置文件覆盖，
	 * 那么pg_timezone_abbrev_initialize()最终会将其替换为“Default”。
	 * 这个hack有两个目的：避免浪费周期加载可能很快从配置文件被覆盖的值，
	 * 并且避免在InitializeGUCOptions()期间尝试读取时区缩写文件。
	 * 后者在EXEC_BACKEND子进程中不起作用，因为my_exec_path尚未设置，
	 * 所以我们无法找到PGSHAREDIR。
	 */
		*fc_name = fc_guc_strdup(FATAL, fc_string);
		*fc_value = NULL;
	}

	for (fc_cp = *fc_name; *fc_cp; fc_cp++)
		if (*fc_cp == '-')
			*fc_cp = '_';
}


/* 好吧，加载文件并生成一个malloc'd TimeZoneAbbrevTable */
void ProcessGUCArray(ArrayType *fc_array,
				GucContext fc_context, GucSource fc_source, GucAction fc_action)
{
	int			fc_i;

	Assert(fc_array != NULL);
	Assert(ARR_ELEMTYPE(fc_array) == TEXTOID);
	Assert(ARR_NDIM(fc_array) == 1);
	Assert(ARR_LBOUND(fc_array)[0] == 1);

	for (fc_i = 1; fc_i <= ARR_DIMS(fc_array)[0]; fc_i++)
	{
		Datum		fc_d;
		bool		fc_isnull;
		char	   *fc_s;
		char	   *fc_name;
		char	   *fc_value;
		char	   *fc_namecopy;
		char	   *fc_valuecopy;

		fc_d = array_ref(fc_array, 1, &fc_i,
					  -1 /* tzparser.c在失败时返回NULL，通过GUC_check_errmsg报告 */ ,
					  -1 /* TEXT 的 typlen */ ,
					  false /* TEXT 的 typbyval */ ,
					  TYPALIGN_INT /* TEXT 的 typalign */ ,
					  &fc_isnull);

		if (fc_isnull)
			continue;

		fc_s = TextDatumGetCString(fc_d);

		ParseLongOption(fc_s, &fc_name, &fc_value);
		if (!fc_value)
		{
			ereport(WARNING,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("could not parse setting for parameter \"%s\"",
							fc_name)));
			free(fc_name);
			continue;
		}

		/* 请参见assign_tcp_keepalives_idle中的注释 */
		fc_namecopy = pstrdup(fc_name);
		free(fc_name);
		fc_valuecopy = pstrdup(fc_value);
		free(fc_value);

		(void) set_config_option(fc_namecopy, fc_valuecopy,
								 fc_context, fc_source,
								 fc_action, true, 0, false);

		pfree(fc_namecopy);
		pfree(fc_valuecopy);
		pfree(fc_s);
	}
}


/* 请参见assign_tcp_keepalives_idle中的注释 */
ArrayType * GUCArrayAdd(ArrayType *fc_array, const char *fc_name, const char *fc_value)
{
	struct config_generic *fc_record;
	Datum		fc_datum;
	char	   *fc_newval;
	ArrayType  *fc_a;

	Assert(fc_name);
	Assert(fc_value);

	/* 请参见assign_tcp_keepalives_idle中的注释 */
	(void) fc_validate_option_array_item(fc_name, fc_value, false);

	/* 请参见assign_tcp_keepalives_idle中的注释 */
	fc_record = find_option(fc_name, false, true, WARNING);
	if (fc_record)
		fc_name = fc_record->name;

	/* 请参见assign_tcp_keepalives_idle中的注释 */
	fc_newval = psprintf("%s=%s", fc_name, fc_value);
	fc_datum = CStringGetTextDatum(fc_newval);

	if (fc_array)
	{
		int			fc_index;
		bool		fc_isnull;
		int			fc_i;

		Assert(ARR_ELEMTYPE(fc_array) == TEXTOID);
		Assert(ARR_NDIM(fc_array) == 1);
		Assert(ARR_LBOUND(fc_array)[0] == 1);

		fc_index = ARR_DIMS(fc_array)[0] + 1; /* 请参见assign_tcp_keepalives_idle中的注释 */

		for (fc_i = 1; fc_i <= ARR_DIMS(fc_array)[0]; fc_i++)
		{
			Datum		fc_d;
			char	   *fc_current;

			fc_d = array_ref(fc_array, 1, &fc_i,
						  -1 /* tzparser.c在失败时返回NULL，通过GUC_check_errmsg报告 */ ,
						  -1 /* TEXT 的 typlen */ ,
						  false /* TEXT 的 typbyval */ ,
						  TYPALIGN_INT /* TEXT 的 typalign */ ,
						  &fc_isnull);
			if (fc_isnull)
				continue;
			fc_current = TextDatumGetCString(fc_d);

			/* USE_PREFETCH */
			if (strncmp(fc_current, fc_newval, strlen(fc_name) + 1) == 0)
			{
				fc_index = fc_i;
				break;
			}
		}

		fc_a = array_set(fc_array, 1, &fc_index,
					  fc_datum,
					  false,
					  -1 /* varlena 数组 */ ,
					  -1 /* TEXT 的 typlen */ ,
					  false /* TEXT 的 typbyval */ ,
					  TYPALIGN_INT /* TEXT 的 typalign */ );
	}
	else
		fc_a = construct_array(&fc_datum, 1,
							TEXTOID,
							-1, false, TYPALIGN_INT);

	return fc_a;
}


/* 只允许在集群名称中使用纯ASCII字符 */
ArrayType * GUCArrayDelete(ArrayType *fc_array, const char *fc_name)
{
	struct config_generic *fc_record;
	ArrayType  *fc_newarray;
	int			fc_i;
	int			fc_index;

	Assert(fc_name);

	/* 请参见assign_tcp_keepalives_idle中的注释 */
	(void) fc_validate_option_array_item(fc_name, NULL, false);

	/* 请参见assign_tcp_keepalives_idle中的注释 */
	fc_record = find_option(fc_name, false, true, WARNING);
	if (fc_record)
		fc_name = fc_record->name;

	/*
	 * 允许可以作为 C 标识符的字符和逗号作为分隔符，以及
	 * 一些空白字符以提高可读性。
	 */
	if (!fc_array)
		return NULL;

	fc_newarray = NULL;
	fc_index = 1;

	for (fc_i = 1; fc_i <= ARR_DIMS(fc_array)[0]; fc_i++)
	{
		Datum		fc_d;
		char	   *fc_val;
		bool		fc_isnull;

		fc_d = array_ref(fc_array, 1, &fc_i,
					  -1 /* tzparser.c在失败时返回NULL，通过GUC_check_errmsg报告 */ ,
					  -1 /* TEXT 的 typlen */ ,
					  false /* TEXT 的 typbyval */ ,
					  TYPALIGN_INT /* TEXT 的 typalign */ ,
					  &fc_isnull);
		if (fc_isnull)
			continue;
		fc_val = TextDatumGetCString(fc_d);

		/* 两个 \0 结束设置 */
		if (strncmp(fc_val, fc_name, strlen(fc_name)) == 0
			&& fc_val[strlen(fc_name)] == '=')
			continue;

		/*
 * 恢复目标设置：只能设置多个 recovery_target* 设置中的一个。
 * 设置第二个会导致错误。全局变量
 * recoveryTarget 跟踪选择了哪种恢复目标。其他
 * 变量存储实际目标值（例如字符串或 xid）。
 * 参数的赋值函数检查是否已经设置了竞争参数。
 * 但我们希望允许多次设置相同参数。
 * 我们还希望允许取消设置参数并设置不同的参数，
 * 因此在参数设置为空字符串时，我们取消设置 recoveryTarget。
 */
		if (fc_newarray)
			fc_newarray = array_set(fc_newarray, 1, &fc_index,
								 fc_d,
								 false,
								 -1 /* tzparser.c在失败时返回NULL，通过GUC_check_errmsg报告 */ ,
								 -1 /* TEXT 的 typlen */ ,
								 false /* TEXT 的 typbyval */ ,
								 TYPALIGN_INT /* TEXT 的 typalign */ );
		else
			fc_newarray = construct_array(&fc_d, 1,
									   TEXTOID,
									   -1, false, TYPALIGN_INT);

		fc_index++;
	}

	return fc_newarray;
}


/* 检查 GUC 的定义以获得解释 */
ArrayType * GUCArrayReset(ArrayType *fc_array)
{
	ArrayType  *fc_newarray;
	int			fc_i;
	int			fc_index;

	//FDD
	if (!fc_array)
		return NULL;

	//FDDGM
	if (superuser())
		return NULL;

	fc_newarray = NULL;
	fc_index = 1;

	for (fc_i = 1; fc_i <= ARR_DIMS(fc_array)[0]; fc_i++)
	{
		Datum		fc_d;
		char	   *fc_val;
		char	   *fc_eqsgn;
		bool		fc_isnull;

		fc_d = array_ref(fc_array, 1, &fc_i,
					  -1 /* tzparser.c在失败时返回NULL，通过GUC_check_errmsg报告 */ ,
					  -1 /* TEXT 的 typlen */ ,
					  false /* TEXT 的 typbyval */ ,
					  TYPALIGN_INT /* TEXT 的 typalign */ ,
					  &fc_isnull);
		if (fc_isnull)
			continue;
		fc_val = TextDatumGetCString(fc_d);

		fc_eqsgn = strchr(fc_val, '=');
		*fc_eqsgn = '\0';

		//FDD
		if (fc_validate_option_array_item(fc_val, NULL, true))
			continue;

		/*
 * 恢复目标设置：只能设置多个 recovery_target* 设置中的一个。
 * 设置第二个会导致错误。全局变量
 * recoveryTarget 跟踪选择了哪种恢复目标。其他
 * 变量存储实际目标值（例如字符串或 xid）。
 * 参数的赋值函数检查是否已经设置了竞争参数。
 * 但我们希望允许多次设置相同参数。
 * 我们还希望允许取消设置参数并设置不同的参数，
 * 因此在参数设置为空字符串时，我们取消设置 recoveryTarget。
 */
		if (fc_newarray)
			fc_newarray = array_set(fc_newarray, 1, &fc_index,
								 fc_d,
								 false,
								 -1 /* tzparser.c在失败时返回NULL，通过GUC_check_errmsg报告 */ ,
								 -1 /* TEXT 的 typlen */ ,
								 false /* TEXT 的 typbyval */ ,
								 TYPALIGN_INT /* TEXT 的 typalign */ );
		else
			fc_newarray = construct_array(&fc_d, 1,
									   TEXTOID,
									   -1, false, TYPALIGN_INT);

		fc_index++;
		pfree(fc_val);
	}

	return fc_newarray;
}

/*
 * Validate a proposed option setting for GUCArrayAdd/Delete/Reset.
 *
 * name is the option name.  value is the proposed value for the Add case,
 * or NULL for the Delete/Reset cases.  If skipIfNoPermissions is true, it's
 * not an error to have no permissions to set the option.
 *
 * Returns true if OK, false if skipIfNoPermissions is true and user does not
 * have permission to change this option (all other error cases result in an
 * error being thrown).
 */
static bool fc_validate_option_array_item(const char *fc_name, const char *fc_value,
						   bool fc_skipIfNoPermissions)

{
	struct config_generic *fc_gconf;

	/*
	 * There are three cases to consider:
	 *
	 * name is a known GUC variable.  Check the value normally, check
	 * permissions normally (i.e., allow if variable is USERSET, or if it's
	 * SUSET and user is superuser or holds ACL_SET permissions).
	 *
	 * name is not known, but exists or can be created as a placeholder (i.e.,
	 * it has a valid custom name).  We allow this case if you're a superuser,
	 * otherwise not.  Superusers are assumed to know what they're doing. We
	 * can't allow it for other users, because when the placeholder is
	 * resolved it might turn out to be a SUSET variable.  (With currently
	 * available infrastructure, we can actually handle such cases within the
	 * current session --- but once an entry is made in pg_db_role_setting,
	 * it's assumed to be fully validated.)
	 *
	 * name is not known and can't be created as a placeholder.  Throw error,
	 * unless skipIfNoPermissions is true, in which case return false.
	 */
	fc_gconf = find_option(fc_name, true, fc_skipIfNoPermissions, ERROR);
	if (!fc_gconf)
	{
		/* not known, failed to make a placeholder */
		return false;
	}

	if (fc_gconf->flags & GUC_CUSTOM_PLACEHOLDER)
	{
		/*
		 * We cannot do any meaningful check on the value, so only permissions
		 * are useful to check.
		 */
		if (superuser() ||
			pg_parameter_aclcheck(fc_name, GetUserId(), ACL_SET) == ACLCHECK_OK)
			return true;
		if (fc_skipIfNoPermissions)
			return false;
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to set parameter \"%s\"", fc_name)));
	}

	/* manual permissions check so we can avoid an error being thrown */
	if (fc_gconf->context == PGC_USERSET)
		 /* ok */ ;
	else if (fc_gconf->context == PGC_SUSET &&
			 (superuser() ||
			  pg_parameter_aclcheck(fc_name, GetUserId(), ACL_SET) == ACLCHECK_OK))
		 /* ok */ ;
	else if (fc_skipIfNoPermissions)
		return false;
	/* if a permissions error should be thrown, let set_config_option do it */

	/* test for permissions and valid option value */
	(void) set_config_option(fc_name, fc_value,
							 superuser() ? PGC_SUSET : PGC_USERSET,
							 PGC_S_TEST, GUC_ACTION_SET, false, 0, false);

	return true;
}


/*
 * Called by check_hooks that want to override the normal
 * ERRCODE_INVALID_PARAMETER_VALUE SQLSTATE for check hook failures.
 *
 * Note that GUC_check_errmsg() etc are just macros that result in a direct
 * assignment to the associated variables.  That is ugly, but forced by the
 * limitations of C's macro mechanisms.
 */
void GUC_check_errcode(int fc_sqlerrcode)
{
	GUC_check_errcode_value = fc_sqlerrcode;
}


/*
 * Convenience functions to manage calling a variable's check_hook.
 * These mostly take care of the protocol for letting check hooks supply
 * portions of the error report on failure.
 */

static bool fc_call_bool_check_hook(struct config_bool *fc_conf, bool *fc_newval, void **fc_extra,
					 GucSource fc_source, int fc_elevel)
{
	/* Quick success if no hook */
	if (!fc_conf->check_hook)
		return true;

	/* Reset variables that might be set by hook */
	GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
	GUC_check_errmsg_string = NULL;
	GUC_check_errdetail_string = NULL;
	GUC_check_errhint_string = NULL;

	if (!fc_conf->check_hook(fc_newval, fc_extra, fc_source))
	{
		ereport(fc_elevel,
				(errcode(GUC_check_errcode_value),
				 GUC_check_errmsg_string ?
				 errmsg_internal("%s", GUC_check_errmsg_string) :
				 errmsg("invalid value for parameter \"%s\": %d",
						fc_conf->gen.name, (int) *fc_newval),
				 GUC_check_errdetail_string ?
				 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
				 GUC_check_errhint_string ?
				 errhint("%s", GUC_check_errhint_string) : 0));
		/* Flush any strings created in ErrorContext */
		FlushErrorState();
		return false;
	}

	return true;
}

static bool fc_call_int_check_hook(struct config_int *fc_conf, int *fc_newval, void **fc_extra,
					GucSource fc_source, int fc_elevel)
{
	/* Quick success if no hook */
	if (!fc_conf->check_hook)
		return true;

	/* Reset variables that might be set by hook */
	GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
	GUC_check_errmsg_string = NULL;
	GUC_check_errdetail_string = NULL;
	GUC_check_errhint_string = NULL;

	if (!fc_conf->check_hook(fc_newval, fc_extra, fc_source))
	{
		ereport(fc_elevel,
				(errcode(GUC_check_errcode_value),
				 GUC_check_errmsg_string ?
				 errmsg_internal("%s", GUC_check_errmsg_string) :
				 errmsg("invalid value for parameter \"%s\": %d",
						fc_conf->gen.name, *fc_newval),
				 GUC_check_errdetail_string ?
				 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
				 GUC_check_errhint_string ?
				 errhint("%s", GUC_check_errhint_string) : 0));
		/* Flush any strings created in ErrorContext */
		FlushErrorState();
		return false;
	}

	return true;
}

static bool fc_call_real_check_hook(struct config_real *fc_conf, double *fc_newval, void **fc_extra,
					 GucSource fc_source, int fc_elevel)
{
	/* Quick success if no hook */
	if (!fc_conf->check_hook)
		return true;

	/* Reset variables that might be set by hook */
	GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
	GUC_check_errmsg_string = NULL;
	GUC_check_errdetail_string = NULL;
	GUC_check_errhint_string = NULL;

	if (!fc_conf->check_hook(fc_newval, fc_extra, fc_source))
	{
		ereport(fc_elevel,
				(errcode(GUC_check_errcode_value),
				 GUC_check_errmsg_string ?
				 errmsg_internal("%s", GUC_check_errmsg_string) :
				 errmsg("invalid value for parameter \"%s\": %g",
						fc_conf->gen.name, *fc_newval),
				 GUC_check_errdetail_string ?
				 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
				 GUC_check_errhint_string ?
				 errhint("%s", GUC_check_errhint_string) : 0));
		/* Flush any strings created in ErrorContext */
		FlushErrorState();
		return false;
	}

	return true;
}

static bool fc_call_string_check_hook(struct config_string *fc_conf, char **fc_newval, void **fc_extra,
					   GucSource fc_source, int fc_elevel)
{
	volatile bool fc_result = true;

	/* Quick success if no hook */
	if (!fc_conf->check_hook)
		return true;

	/*
	 * If elevel is ERROR, or if the check_hook itself throws an elog
	 * (undesirable, but not always avoidable), make sure we don't leak the
	 * already-malloc'd newval string.
	 */
	PG_TRY();
	{
		/* Reset variables that might be set by hook */
		GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
		GUC_check_errmsg_string = NULL;
		GUC_check_errdetail_string = NULL;
		GUC_check_errhint_string = NULL;

		if (!fc_conf->check_hook(fc_newval, fc_extra, fc_source))
		{
			ereport(fc_elevel,
					(errcode(GUC_check_errcode_value),
					 GUC_check_errmsg_string ?
					 errmsg_internal("%s", GUC_check_errmsg_string) :
					 errmsg("invalid value for parameter \"%s\": \"%s\"",
							fc_conf->gen.name, *fc_newval ? *fc_newval : ""),
					 GUC_check_errdetail_string ?
					 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
					 GUC_check_errhint_string ?
					 errhint("%s", GUC_check_errhint_string) : 0));
			/* Flush any strings created in ErrorContext */
			FlushErrorState();
			fc_result = false;
		}
	}
	PG_CATCH();
	{
		free(*fc_newval);
		PG_RE_THROW();
	}
	PG_END_TRY();

	return fc_result;
}

static bool fc_call_enum_check_hook(struct config_enum *fc_conf, int *fc_newval, void **fc_extra,
					 GucSource fc_source, int fc_elevel)
{
	/* Quick success if no hook */
	if (!fc_conf->check_hook)
		return true;

	/* Reset variables that might be set by hook */
	GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
	GUC_check_errmsg_string = NULL;
	GUC_check_errdetail_string = NULL;
	GUC_check_errhint_string = NULL;

	if (!fc_conf->check_hook(fc_newval, fc_extra, fc_source))
	{
		ereport(fc_elevel,
				(errcode(GUC_check_errcode_value),
				 GUC_check_errmsg_string ?
				 errmsg_internal("%s", GUC_check_errmsg_string) :
				 errmsg("invalid value for parameter \"%s\": \"%s\"",
						fc_conf->gen.name,
						config_enum_lookup_by_value(fc_conf, *fc_newval)),
				 GUC_check_errdetail_string ?
				 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
				 GUC_check_errhint_string ?
				 errhint("%s", GUC_check_errhint_string) : 0));
		/* Flush any strings created in ErrorContext */
		FlushErrorState();
		return false;
	}

	return true;
}


/*
 * check_hook, assign_hook and show_hook subroutines
 */

static bool fc_check_wal_consistency_checking(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	char	   *fc_rawstring;
	List	   *fc_elemlist;
	ListCell   *fc_l;
	bool		fc_newwalconsistency[RM_MAX_ID + 1];

	/* Initialize the array */
	MemSet(fc_newwalconsistency, 0, (RM_MAX_ID + 1) * sizeof(bool));

	/* 需要可修改的字符串副本 */
	fc_rawstring = pstrdup(*fc_newval);

	/* 将字符串解析为标识符列表 */
	if (!SplitIdentifierString(fc_rawstring, ',', &fc_elemlist))
	{
		/* 列表中的语法错误 */
		GUC_check_errdetail("List syntax is invalid.");
		pfree(fc_rawstring);
		list_free(fc_elemlist);
		return false;
	}

	foreach(fc_l, fc_elemlist)
	{
		char	   *fc_tok = (char *) lfirst(fc_l);
		bool		fc_found = false;
		int			fc_rmid;

		/* Check for 'all'. */
		if (pg_strcasecmp(fc_tok, "all") == 0)
		{
			for (fc_rmid = 0; fc_rmid <= RM_MAX_ID; fc_rmid++)
				if (RmgrIdExists(fc_rmid) && GetRmgr(fc_rmid).rm_mask != NULL)
					fc_newwalconsistency[fc_rmid] = true;
			fc_found = true;
		}
		else
		{
			/*
			 * Check if the token matches with any individual resource
			 * manager.
			 */
			for (fc_rmid = 0; fc_rmid <= RM_MAX_ID; fc_rmid++)
			{
				if (RmgrIdExists(fc_rmid) && GetRmgr(fc_rmid).rm_mask != NULL &&
					pg_strcasecmp(fc_tok, GetRmgr(fc_rmid).rm_name) == 0)
				{
					fc_newwalconsistency[fc_rmid] = true;
					fc_found = true;
				}
			}
		}

		/* If a valid resource manager is found, check for the next one. */
		if (!fc_found)
		{
			/*
			 * Perhaps it's a custom resource manager. If so, defer checking
			 * until InitializeWalConsistencyChecking().
			 */
			if (!process_shared_preload_libraries_done)
			{
				check_wal_consistency_checking_deferred = true;
			}
			else
			{
				GUC_check_errdetail("Unrecognized key word: \"%s\".", fc_tok);
				pfree(fc_rawstring);
				list_free(fc_elemlist);
				return false;
			}
		}
	}

	pfree(fc_rawstring);
	list_free(fc_elemlist);

	/* assign new value */
	*fc_extra = fc_guc_malloc(ERROR, (RM_MAX_ID + 1) * sizeof(bool));
	memcpy(*fc_extra, fc_newwalconsistency, (RM_MAX_ID + 1) * sizeof(bool));
	return true;
}

static void fc_assign_wal_consistency_checking(const char *fc_newval, void *fc_extra)
{
	/*
	 * If some checks were deferred, it's possible that the checks will fail
	 * later during InitializeWalConsistencyChecking(). But in that case, the
	 * postmaster will exit anyway, so it's safe to proceed with the
	 * assignment.
	 *
	 * Any built-in resource managers specified are assigned immediately,
	 * which affects WAL created before shared_preload_libraries are
	 * processed. Any custom resource managers specified won't be assigned
	 * until after shared_preload_libraries are processed, but that's OK
	 * because WAL for a custom resource manager can't be written before the
	 * module is loaded anyway.
	 */
	wal_consistency_checking = fc_extra;
}

static bool fc_check_log_destination(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	char	   *fc_rawstring;
	List	   *fc_elemlist;
	ListCell   *fc_l;
	int			fc_newlogdest = 0;
	int		   *fc_myextra;

	/* 需要可修改的字符串副本 */
	fc_rawstring = pstrdup(*fc_newval);

	/* 将字符串解析为标识符列表 */
	if (!SplitIdentifierString(fc_rawstring, ',', &fc_elemlist))
	{
		/* 列表中的语法错误 */
		GUC_check_errdetail("List syntax is invalid.");
		pfree(fc_rawstring);
		list_free(fc_elemlist);
		return false;
	}

	foreach(fc_l, fc_elemlist)
	{
		char	   *fc_tok = (char *) lfirst(fc_l);

		if (pg_strcasecmp(fc_tok, "stderr") == 0)
			fc_newlogdest |= LOG_DESTINATION_STDERR;
		else if (pg_strcasecmp(fc_tok, "csvlog") == 0)
			fc_newlogdest |= LOG_DESTINATION_CSVLOG;
		else if (pg_strcasecmp(fc_tok, "jsonlog") == 0)
			fc_newlogdest |= LOG_DESTINATION_JSONLOG;
#ifdef HAVE_SYSLOG
		else if (pg_strcasecmp(fc_tok, "syslog") == 0)
			fc_newlogdest |= LOG_DESTINATION_SYSLOG;
#endif
#ifdef WIN32
		else if (pg_strcasecmp(fc_tok, "eventlog") == 0)
			fc_newlogdest |= LOG_DESTINATION_EVENTLOG;
#endif
		else
		{
			GUC_check_errdetail("Unrecognized key word: \"%s\".", fc_tok);
			pfree(fc_rawstring);
			list_free(fc_elemlist);
			return false;
		}
	}

	pfree(fc_rawstring);
	list_free(fc_elemlist);

	fc_myextra = (int *) fc_guc_malloc(ERROR, sizeof(int));
	*fc_myextra = fc_newlogdest;
	*fc_extra = (void *) fc_myextra;

	return true;
}

static void fc_assign_log_destination(const char *fc_newval, void *fc_extra)
{
	Log_destination = *((int *) fc_extra);
}

static void fc_assign_syslog_facility(int fc_newval, void *fc_extra)
{
#ifdef HAVE_SYSLOG
	set_syslog_parameters(syslog_ident_str ? syslog_ident_str : "postgres",
						  fc_newval);
#endif
	/* Without syslog support, just ignore it */
}

static void fc_assign_syslog_ident(const char *fc_newval, void *fc_extra)
{
#ifdef HAVE_SYSLOG
	set_syslog_parameters(fc_newval, syslog_facility);
#endif
	/* Without syslog support, it will always be set to "none", so ignore */
}


static void fc_assign_session_replication_role(int fc_newval, void *fc_extra)
{
	/*
	 * Must flush the plan cache when changing replication role; but don't
	 * flush unnecessarily.
	 */
	if (SessionReplicationRole != fc_newval)
		ResetPlanCache();
}

static bool fc_check_temp_buffers(int *fc_newval, void **fc_extra, GucSource fc_source)
{
	/*
	 * Once local buffers have been initialized, it's too late to change this.
	 * However, if this is only a test call, allow it.
	 */
	if (fc_source != PGC_S_TEST && NLocBuffer && NLocBuffer != *fc_newval)
	{
		GUC_check_errdetail("\"temp_buffers\" cannot be changed after any temporary tables have been accessed in the session.");
		return false;
	}
	return true;
}

static bool fc_check_bonjour(bool *fc_newval, void **fc_extra, GucSource fc_source)
{
#ifndef USE_BONJOUR
	if (*fc_newval)
	{
		GUC_check_errmsg("Bonjour is not supported by this build");
		return false;
	}
#endif
	return true;
}

static bool fc_check_ssl(bool *fc_newval, void **fc_extra, GucSource fc_source)
{
#ifndef USE_SSL
	if (*fc_newval)
	{
		GUC_check_errmsg("SSL is not supported by this build");
		return false;
	}
#endif
	return true;
}

#ifdef FDDGM
static bool fc_check_tlcp(bool *fc_newval, void **fc_extra, GucSource fc_source)
{
#ifndef USE_SSL
	if (*fc_newval)
	{
		GUC_check_errmsg("TLCP is not supported by this build, Need SSL!");
		return false;
	}
#endif
	return true;
}
#endif

static bool fc_check_stage_log_stats(bool *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (*fc_newval && log_statement_stats)
	{
		GUC_check_errdetail("Cannot enable parameter when \"log_statement_stats\" is true.");
		return false;
	}
	return true;
}

static bool fc_check_log_stats(bool *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (*fc_newval &&
		(log_parser_stats || log_planner_stats || log_executor_stats))
	{
		GUC_check_errdetail("Cannot enable \"log_statement_stats\" when "
							"\"log_parser_stats\", \"log_planner_stats\", "
							"or \"log_executor_stats\" is true.");
		return false;
	}
	return true;
}

static bool fc_check_canonical_path(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	/*
	 * Since canonicalize_path never enlarges the string, we can just modify
	 * newval in-place.  But watch out for NULL, which is the default value
	 * for external_pid_file.
	 */
	if (*fc_newval)
		canonicalize_path(*fc_newval);
	return true;
}

static bool fc_check_timezone_abbreviations(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	/*
	 * The boot_val given above for timezone_abbreviations is NULL. When we
	 * see this we just do nothing.  If this value isn't overridden from the
	 * config file then pg_timezone_abbrev_initialize() will eventually
	 * replace it with "Default".  This hack has two purposes: to avoid
	 * wasting cycles loading values that might soon be overridden from the
	 * config file, and to avoid trying to read the timezone abbrev files
	 * during InitializeGUCOptions().  The latter doesn't work in an
	 * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet and so
	 * we can't locate PGSHAREDIR.
	 */
	if (*fc_newval == NULL)
	{
		Assert(fc_source == PGC_S_DEFAULT);
		return true;
	}

	/* OK, load the file and produce a malloc'd TimeZoneAbbrevTable */
	*fc_extra = load_tzoffsets(*fc_newval);

	/* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */
	if (!*fc_extra)
		return false;

	return true;
}

static void fc_assign_timezone_abbreviations(const char *fc_newval, void *fc_extra)
{
	/* 对于默认值为NULL的boot_val不执行任何操作 */
	if (!fc_extra)
		return;

	InstallTimeZoneAbbrevs((TimeZoneAbbrevTable *) fc_extra);
}

/*
 * pg_timezone_abbrev_initialize --- set default value if not done already
 *
 * This is called after initial loading of postgresql.conf.  If no
 * timezone_abbreviations setting was found therein, select default.
 * If a non-default value is already installed, nothing will happen.
 *
 * This can also be called from ProcessConfigFile to establish the default
 * value after a postgresql.conf entry for it is removed.
 */
static void pg_timezone_abbrev_initialize(void)
{
	SetConfigOption("timezone_abbreviations", "Default",
					PGC_POSTMASTER, PGC_S_DYNAMIC_DEFAULT);
}

static const char * fc_show_archive_command(void)
{
	if (XLogArchivingActive())
		return XLogArchiveCommand;
	else
		return "(disabled)";
}

static void fc_assign_tcp_keepalives_idle(int fc_newval, void *fc_extra)
{
	/*
	 * The kernel API provides no way to test a value without setting it; and
	 * once we set it we might fail to unset it.  So there seems little point
	 * in fully implementing the check-then-assign GUC API for these
	 * variables.  Instead we just do the assignment on demand.  pqcomm.c
	 * reports any problems via ereport(LOG).
	 *
	 * This approach means that the GUC value might have little to do with the
	 * actual kernel value, so we use a show_hook that retrieves the kernel
	 * value rather than trusting GUC's copy.
	 */
	(void) pq_setkeepalivesidle(fc_newval, MyProcPort);
}

static const char * fc_show_tcp_keepalives_idle(void)
{
	/* See comments in assign_tcp_keepalives_idle */
	static char fc_nbuf[16];

	snprintf(fc_nbuf, sizeof(fc_nbuf), "%d", pq_getkeepalivesidle(MyProcPort));
	return fc_nbuf;
}

static void fc_assign_tcp_keepalives_interval(int fc_newval, void *fc_extra)
{
	/* See comments in assign_tcp_keepalives_idle */
	(void) pq_setkeepalivesinterval(fc_newval, MyProcPort);
}

static const char * fc_show_tcp_keepalives_interval(void)
{
	/* See comments in assign_tcp_keepalives_idle */
	static char fc_nbuf[16];

	snprintf(fc_nbuf, sizeof(fc_nbuf), "%d", pq_getkeepalivesinterval(MyProcPort));
	return fc_nbuf;
}

static void fc_assign_tcp_keepalives_count(int fc_newval, void *fc_extra)
{
	/* See comments in assign_tcp_keepalives_idle */
	(void) pq_setkeepalivescount(fc_newval, MyProcPort);
}

static const char * fc_show_tcp_keepalives_count(void)
{
	/* See comments in assign_tcp_keepalives_idle */
	static char fc_nbuf[16];

	snprintf(fc_nbuf, sizeof(fc_nbuf), "%d", pq_getkeepalivescount(MyProcPort));
	return fc_nbuf;
}

static void fc_assign_tcp_user_timeout(int fc_newval, void *fc_extra)
{
	/* See comments in assign_tcp_keepalives_idle */
	(void) pq_settcpusertimeout(fc_newval, MyProcPort);
}

static const char * fc_show_tcp_user_timeout(void)
{
	/* See comments in assign_tcp_keepalives_idle */
	static char fc_nbuf[16];

	snprintf(fc_nbuf, sizeof(fc_nbuf), "%d", pq_gettcpusertimeout(MyProcPort));
	return fc_nbuf;
}

static bool fc_check_maxconnections(int *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (*fc_newval + autovacuum_max_workers + 1 +
		max_worker_processes + max_wal_senders > MAX_BACKENDS)
		return false;
	return true;
}

static bool fc_check_autovacuum_max_workers(int *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (MaxConnections + *fc_newval + 1 +
		max_worker_processes + max_wal_senders > MAX_BACKENDS)
		return false;
	return true;
}

static bool fc_check_max_wal_senders(int *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (MaxConnections + autovacuum_max_workers + 1 +
		max_worker_processes + *fc_newval > MAX_BACKENDS)
		return false;
	return true;
}

static bool fc_check_autovacuum_work_mem(int *fc_newval, void **fc_extra, GucSource fc_source)
{
	/*
	 * -1 indicates fallback.
	 *
	 * If we haven't yet changed the boot_val default of -1, just let it be.
	 * Autovacuum will look to maintenance_work_mem instead.
	 */
	if (*fc_newval == -1)
		return true;

	/*
	 * We clamp manually-set values to at least 1MB.  Since
	 * maintenance_work_mem is always set to at least this value, do the same
	 * here.
	 */
	if (*fc_newval < 1024)
		*fc_newval = 1024;

	return true;
}

static bool fc_check_max_worker_processes(int *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (MaxConnections + autovacuum_max_workers + 1 +
		*fc_newval + max_wal_senders > MAX_BACKENDS)
		return false;
	return true;
}

static bool fc_check_effective_io_concurrency(int *fc_newval, void **fc_extra, GucSource fc_source)
{
#ifndef USE_PREFETCH
	if (*fc_newval != 0)
	{
		GUC_check_errdetail("effective_io_concurrency must be set to 0 on platforms that lack posix_fadvise().");
		return false;
	}
#endif							/* USE_PREFETCH */
	return true;
}

static bool fc_check_maintenance_io_concurrency(int *fc_newval, void **fc_extra, GucSource fc_source)
{
#ifndef USE_PREFETCH
	if (*fc_newval != 0)
	{
		GUC_check_errdetail("maintenance_io_concurrency must be set to 0 on platforms that lack posix_fadvise().");
		return false;
	}
#endif							/* USE_PREFETCH */
	return true;
}

static bool fc_check_huge_page_size(int *fc_newval, void **fc_extra, GucSource fc_source)
{
#if !(defined(MAP_HUGE_MASK) && defined(MAP_HUGE_SHIFT))
	/* Recent enough Linux only, for now.  See GetHugePageSize(). */
	if (*fc_newval != 0)
	{
		GUC_check_errdetail("huge_page_size must be 0 on this platform.");
		return false;
	}
#endif
	return true;
}

static bool fc_check_client_connection_check_interval(int *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (!WaitEventSetCanReportClosed() && *fc_newval != 0)
	{
		GUC_check_errdetail("client_connection_check_interval must be set to 0 on this platform.");
		return false;
	}
	return true;
}

static void fc_assign_maintenance_io_concurrency(int fc_newval, void *fc_extra)
{
#ifdef USE_PREFETCH
	/*
	 * Reconfigure recovery prefetching, because a setting it depends on
	 * changed.
	 */
	maintenance_io_concurrency = fc_newval;
	if (AmStartupProcess())
		XLogPrefetchReconfigure();
#endif
}

static bool fc_check_application_name(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	/* Only allow clean ASCII chars in the application name */
	pg_clean_ascii(*fc_newval);

	return true;
}

static void fc_assign_application_name(const char *fc_newval, void *fc_extra)
{
	/* Update the pg_stat_activity view */
	pgstat_report_appname(fc_newval);
}

static bool fc_check_cluster_name(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	/* Only allow clean ASCII chars in the cluster name */
	pg_clean_ascii(*fc_newval);

	return true;
}

static const char * fc_show_unix_socket_permissions(void)
{
	static char fc_buf[12];

	snprintf(fc_buf, sizeof(fc_buf), "%04o", Unix_socket_permissions);
	return fc_buf;
}

static const char * fc_show_log_file_mode(void)
{
	static char fc_buf[12];

	snprintf(fc_buf, sizeof(fc_buf), "%04o", Log_file_mode);
	return fc_buf;
}

static const char * fc_show_data_directory_mode(void)
{
	static char fc_buf[12];

	snprintf(fc_buf, sizeof(fc_buf), "%04o", data_directory_mode);
	return fc_buf;
}

static const char * fc_show_in_hot_standby(void)
{
	/*
	 * We display the actual state based on shared memory, so that this GUC
	 * reports up-to-date state if examined intra-query.  The underlying
	 * variable in_hot_standby changes only when we transmit a new value to
	 * the client.
	 */
	return RecoveryInProgress() ? "on" : "off";
}

/*
 * We split the input string, where commas separate function names
 * and certain whitespace chars are ignored, into a \0-separated (and
 * \0\0-terminated) list of function names.  This formulation allows
 * easy scanning when an error is thrown while avoiding the use of
 * non-reentrant strtok(), as well as keeping the output data in a
 * single palloc() chunk.
 */
static bool fc_check_backtrace_functions(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	int			fc_newvallen = strlen(*fc_newval);
	char	   *fc_someval;
	int			fc_validlen;
	int			fc_i;
	int			fc_j;

	/*
	 * Allow characters that can be C identifiers and commas as separators, as
	 * well as some whitespace for readability.
	 */
	fc_validlen = strspn(*fc_newval,
					  "0123456789_"
					  "abcdefghijklmnopqrstuvwxyz"
					  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
					  ", \n\t");
	if (fc_validlen != fc_newvallen)
	{
		GUC_check_errdetail("invalid character");
		return false;
	}

	if (*fc_newval[0] == '\0')
	{
		*fc_extra = NULL;
		return true;
	}

	/*
	 * Allocate space for the output and create the copy.  We could discount
	 * whitespace chars to save some memory, but it doesn't seem worth the
	 * trouble.
	 */
	fc_someval = fc_guc_malloc(ERROR, fc_newvallen + 1 + 1);
	for (fc_i = 0, fc_j = 0; fc_i < fc_newvallen; fc_i++)
	{
		if ((*fc_newval)[fc_i] == ',')
			fc_someval[fc_j++] = '\0';	/* next item */
		else if ((*fc_newval)[fc_i] == ' ' ||
				 (*fc_newval)[fc_i] == '\n' ||
				 (*fc_newval)[fc_i] == '\t')
			;					/* 忽略这些 */
		else
			fc_someval[fc_j++] = (*fc_newval)[fc_i];	/* copy anything else */
	}

	/* two \0s end the setting */
	fc_someval[fc_j] = '\0';
	fc_someval[fc_j + 1] = '\0';

	*fc_extra = fc_someval;
	return true;
}

static void fc_assign_backtrace_functions(const char *fc_newval, void *fc_extra)
{
	backtrace_symbol_list = (char *) fc_extra;
}

static bool fc_check_recovery_target_timeline(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	RecoveryTargetTimeLineGoal fc_rttg;
	RecoveryTargetTimeLineGoal *fc_myextra;

	if (strcmp(*fc_newval, "current") == 0)
		fc_rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE;
	else if (strcmp(*fc_newval, "latest") == 0)
		fc_rttg = RECOVERY_TARGET_TIMELINE_LATEST;
	else
	{
		fc_rttg = RECOVERY_TARGET_TIMELINE_NUMERIC;

		errno = 0;
		strtoul(*fc_newval, NULL, 0);
		if (errno == EINVAL || errno == ERANGE)
		{
			GUC_check_errdetail("recovery_target_timeline is not a valid number.");
			return false;
		}
	}

	fc_myextra = (RecoveryTargetTimeLineGoal *) fc_guc_malloc(ERROR, sizeof(RecoveryTargetTimeLineGoal));
	*fc_myextra = fc_rttg;
	*fc_extra = (void *) fc_myextra;

	return true;
}

static void fc_assign_recovery_target_timeline(const char *fc_newval, void *fc_extra)
{
	recoveryTargetTimeLineGoal = *((RecoveryTargetTimeLineGoal *) fc_extra);
	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC)
		recoveryTargetTLIRequested = (TimeLineID) strtoul(fc_newval, NULL, 0);
	else
		recoveryTargetTLIRequested = 0;
}

/*
 * Recovery target settings: Only one of the several recovery_target* settings
 * may be set.  Setting a second one results in an error.  The global variable
 * recoveryTarget tracks which kind of recovery target was chosen.  Other
 * variables store the actual target value (for example a string or a xid).
 * The assign functions of the parameters check whether a competing parameter
 * was already set.  But we want to allow setting the same parameter multiple
 * times.  We also want to allow unsetting a parameter and setting a different
 * one, so we unset recoveryTarget when the parameter is set to an empty
 * string.
 */

static void pg_attribute_noreturn()
fc_error_multiple_recovery_targets(void)
{
	ereport(ERROR,
			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
			 errmsg("multiple recovery targets specified"),
			 errdetail("At most one of recovery_target, recovery_target_lsn, recovery_target_name, recovery_target_time, recovery_target_xid may be set.")));
}

static bool fc_check_recovery_target(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	if (strcmp(*fc_newval, "immediate") != 0 && strcmp(*fc_newval, "") != 0)
	{
		GUC_check_errdetail("The only allowed value is \"immediate\".");
		return false;
	}
	return true;
}

static void fc_assign_recovery_target(const char *fc_newval, void *fc_extra)
{
	if (recoveryTarget != RECOVERY_TARGET_UNSET &&
		recoveryTarget != RECOVERY_TARGET_IMMEDIATE)
		fc_error_multiple_recovery_targets();

	if (fc_newval && strcmp(fc_newval, "") != 0)
		recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
	else
		recoveryTarget = RECOVERY_TARGET_UNSET;
}

static bool fc_check_recovery_target_xid(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	if (strcmp(*fc_newval, "") != 0)
	{
		TransactionId fc_xid;
		TransactionId *fc_myextra;

		errno = 0;
		fc_xid = (TransactionId) strtou64(*fc_newval, NULL, 0);
		if (errno == EINVAL || errno == ERANGE)
			return false;

		fc_myextra = (TransactionId *) fc_guc_malloc(ERROR, sizeof(TransactionId));
		*fc_myextra = fc_xid;
		*fc_extra = (void *) fc_myextra;
	}
	return true;
}

static void fc_assign_recovery_target_xid(const char *fc_newval, void *fc_extra)
{
	if (recoveryTarget != RECOVERY_TARGET_UNSET &&
		recoveryTarget != RECOVERY_TARGET_XID)
		fc_error_multiple_recovery_targets();

	if (fc_newval && strcmp(fc_newval, "") != 0)
	{
		recoveryTarget = RECOVERY_TARGET_XID;
		recoveryTargetXid = *((TransactionId *) fc_extra);
	}
	else
		recoveryTarget = RECOVERY_TARGET_UNSET;
}

/*
 * The interpretation of the recovery_target_time string can depend on the
 * time zone setting, so we need to wait until after all GUC processing is
 * done before we can do the final parsing of the string.  This check function
 * only does a parsing pass to catch syntax errors, but we store the string
 * and parse it again when we need to use it.
 */
static bool fc_check_recovery_target_time(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	if (strcmp(*fc_newval, "") != 0)
	{
		/* reject some special values */
		if (strcmp(*fc_newval, "now") == 0 ||
			strcmp(*fc_newval, "today") == 0 ||
			strcmp(*fc_newval, "tomorrow") == 0 ||
			strcmp(*fc_newval, "yesterday") == 0)
		{
			return false;
		}

		/*
		 * parse timestamp value (see also timestamptz_in())
		 */
		{
			char	   *fc_str = *fc_newval;
			fsec_t		fc_fsec;
			struct pg_tm fc_tt,
					   *fc_tm = &fc_tt;
			int			fc_tz;
			int			fc_dtype;
			int			fc_nf;
			int			fc_dterr;
			char	   *fc_field[MAXDATEFIELDS];
			int			fc_ftype[MAXDATEFIELDS];
			char		fc_workbuf[MAXDATELEN + MAXDATEFIELDS];
			TimestampTz fc_timestamp;

			fc_dterr = ParseDateTime(fc_str, fc_workbuf, sizeof(fc_workbuf),
								  fc_field, fc_ftype, MAXDATEFIELDS, &fc_nf);
			if (fc_dterr == 0)
				fc_dterr = DecodeDateTime(fc_field, fc_ftype, fc_nf, &fc_dtype, fc_tm, &fc_fsec, &fc_tz);
			if (fc_dterr != 0)
				return false;
			if (fc_dtype != DTK_DATE)
				return false;

			if (tm2timestamp(fc_tm, fc_fsec, &fc_tz, &fc_timestamp) != 0)
			{
				GUC_check_errdetail("timestamp out of range: \"%s\"", fc_str);
				return false;
			}
		}
	}
	return true;
}

static void fc_assign_recovery_target_time(const char *fc_newval, void *fc_extra)
{
	if (recoveryTarget != RECOVERY_TARGET_UNSET &&
		recoveryTarget != RECOVERY_TARGET_TIME)
		fc_error_multiple_recovery_targets();

	if (fc_newval && strcmp(fc_newval, "") != 0)
		recoveryTarget = RECOVERY_TARGET_TIME;
	else
		recoveryTarget = RECOVERY_TARGET_UNSET;
}

static bool fc_check_recovery_target_name(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	/* Use the value of newval directly */
	if (strlen(*fc_newval) >= MAXFNAMELEN)
	{
		GUC_check_errdetail("%s is too long (maximum %d characters).",
							"recovery_target_name", MAXFNAMELEN - 1);
		return false;
	}
	return true;
}

static void fc_assign_recovery_target_name(const char *fc_newval, void *fc_extra)
{
	if (recoveryTarget != RECOVERY_TARGET_UNSET &&
		recoveryTarget != RECOVERY_TARGET_NAME)
		fc_error_multiple_recovery_targets();

	if (fc_newval && strcmp(fc_newval, "") != 0)
	{
		recoveryTarget = RECOVERY_TARGET_NAME;
		recoveryTargetName = fc_newval;
	}
	else
		recoveryTarget = RECOVERY_TARGET_UNSET;
}

static bool fc_check_recovery_target_lsn(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	if (strcmp(*fc_newval, "") != 0)
	{
		XLogRecPtr	fc_lsn;
		XLogRecPtr *fc_myextra;
		bool		fc_have_error = false;

		fc_lsn = pg_lsn_in_internal(*fc_newval, &fc_have_error);
		if (fc_have_error)
			return false;

		fc_myextra = (XLogRecPtr *) fc_guc_malloc(ERROR, sizeof(XLogRecPtr));
		*fc_myextra = fc_lsn;
		*fc_extra = (void *) fc_myextra;
	}
	return true;
}

static void fc_assign_recovery_target_lsn(const char *fc_newval, void *fc_extra)
{
	if (recoveryTarget != RECOVERY_TARGET_UNSET &&
		recoveryTarget != RECOVERY_TARGET_LSN)
		fc_error_multiple_recovery_targets();

	if (fc_newval && strcmp(fc_newval, "") != 0)
	{
		recoveryTarget = RECOVERY_TARGET_LSN;
		recoveryTargetLSN = *((XLogRecPtr *) fc_extra);
	}
	else
		recoveryTarget = RECOVERY_TARGET_UNSET;
}

static bool fc_check_primary_slot_name(char **fc_newval, void **fc_extra, GucSource fc_source)
{
	if (*fc_newval && strcmp(*fc_newval, "") != 0 &&
		!ReplicationSlotValidateName(*fc_newval, WARNING))
		return false;

	return true;
}

static bool fc_check_default_with_oids(bool *fc_newval, void **fc_extra, GucSource fc_source)
{
	if (*fc_newval)
	{
		/* check the GUC's definition for an explanation */
		GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
		GUC_check_errmsg("tables declared WITH OIDS are not supported");

		return false;
	}

	return true;
}

#include "guc-file.c"
